Compare commits

...

54 Commits

Author SHA1 Message Date
Ivan Santiago Paunovic
d0cd6bb0a4 13.0.0 2021-08-23 17:46:42 +00:00
Ivan Santiago Paunovic
fd08f0dbe7 Changelogs
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
2021-08-23 17:45:06 +00:00
Ahmed Sobhy
2dd09ae274 remove can_be_nullptr assignment check for QNX case (#1752)
Signed-off-by: Ahmed Sobhy <asobhy@blackberry.com>
2021-08-23 11:19:05 -03:00
Ivan Santiago Paunovic
679fb2ba33 Update client API to be able to remove pending requests (#1734)
* Revert "Revert "Updating client API to be able to remove pending requests (#1728)" (#1733)"

This reverts commit d5f3d35fbe.

Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>

* Address peer review comments

Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>

* Fix tests in rclcpp_components, rclcpp_lifecycle

Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
2021-08-23 09:57:18 -03:00
Alberto Soragna
4fcd05db72 Change log level for lifecycle_publisher (#1715)
* Change log level for lifecycle_publisher

De-activating a lifecycle publisher while the function that was invoking `publish` is still running floods the log of useless warning messages.
This requires to add a boolean check around the publish call, thus making useless the choice of a lifecycle publisher

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

* change lifecycle publisher to log warning only once per transition

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

* rework lifecycle publisher log mechanism to use an helper function

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

* change doxygen format to use implicit brief

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

Co-authored-by: Alberto Soragna <asoragna@irobot.com>
2021-08-19 10:59:57 -07:00
Mauro Passerino
d7764b4322 Fix bug: Add node while spinning
Signed-off-by: Mauro Passerino <mpasserino@irobot.com>
2021-08-18 08:09:58 -07:00
Christophe Bedard
893b9b4f82 Add tracing instrumentation for executor and message taking (#1738)
* Add tracing instrumentation for executor and message taking

Signed-off-by: Christophe Bedard <bedard.christophe@gmail.com>

* Move publisher handle argument to rcl_publish tracepoint

Signed-off-by: Christophe Bedard <bedard.christophe@gmail.com>

* Keep publisher handle arg for rclcpp_publish tracepoint but use nullptr

Signed-off-by: Christophe Bedard <bedard.christophe@gmail.com>
2021-08-12 12:48:35 -07:00
mauropasse
b918bd4c25 Reset timer trigger time before execute. (#1739)
This PR fixes an issue with StaticSingleThreadedExecutor
not reseting the timers, so they were always ready
and executing callbacks continuosly.

https://github.com/ros2/rclcpp/pull/1692

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>
2021-08-09 10:25:04 -03:00
Shane Loretz
3b1144f1e0 Use FindPython3 and make python3 dependency explicit (#1745)
* Use FindPython3 and make python3 dependency explicit

Signed-off-by: Shane Loretz <sloretz@osrfoundation.org>

* Need CMake 3.12 for FindPython3

Signed-off-by: Shane Loretz <sloretz@osrfoundation.org>
2021-08-06 12:08:50 -07:00
Shane Loretz
6c0a46bcc8 Use rosidl_get_typesupport_target() (#1729)
Signed-off-by: Shane Loretz <sloretz@osrfoundation.org>
2021-08-04 16:08:47 -07:00
M. Hofstätter
3cddb4edab Fix returning invalid namespace if sub_namespace is empty (#1658)
* Create valid effective namespace when sub-namespace is empty

Fix #1656.

Signed-off-by: Markus Hofstaetter <markus.hofstaetter@ait.ac.at>

* Add regression test for effective namespace and empty sub-namespace

Adds regression test for #1656.

Signed-off-by: Markus Hofstaetter <markus.hofstaetter@ait.ac.at>
2021-08-04 08:02:51 -07:00
Ivan Santiago Paunovic
d5f3d35fbe Revert "Updating client API to be able to remove pending requests (#1728)" (#1733)
This reverts commit bf752c75f5.

Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
2021-07-29 11:37:57 -03:00
Ivan Santiago Paunovic
bf752c75f5 Updating client API to be able to remove pending requests (#1728)
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
2021-07-29 11:35:06 -03:00
Shane Loretz
01f6ebdd3d RCLCPP_PUBLIC -> RCLCPP_LIFECYCLE_PUBLIC (#1732)
Signed-off-by: Shane Loretz <sloretz@osrfoundation.org>
2021-07-28 15:18:31 -07:00
Karsten Knese
7bf52dd8a6 wait for message (#1705)
* wait for message

Signed-off-by: Karsten Knese <Karsten1987@users.noreply.github.com>

* move to own header file

Signed-off-by: Karsten Knese <Karsten1987@users.noreply.github.com>

* linters

Signed-off-by: Karsten Knese <Karsten1987@users.noreply.github.com>

* add gc for shutdown interrupt

Signed-off-by: Karsten Knese <Karsten1987@users.noreply.github.com>

* mention behavior when shutdown is called

Signed-off-by: Karsten Knese <Karsten1987@users.noreply.github.com>

* check gc

Signed-off-by: Karsten Knese <Karsten1987@users.noreply.github.com>
2021-07-27 23:05:36 -07:00
Christophe Bedard
1b28f389c2 Use rcpputils/scope_exit.hpp and remove rclcpp/scope_exit.hpp (#1727)
* Use rcpputils/scope_exit.hpp and remove rclcpp/scope_exit.hpp

Signed-off-by: Christophe Bedard <bedard.christophe@gmail.com>

* Remove RCLCPP_STRING_JOIN() since it's unused & also provided by rcutils

Signed-off-by: Christophe Bedard <bedard.christophe@gmail.com>

* Revert "Remove RCLCPP_STRING_JOIN() since it's unused & also provided by rcutils"

This reverts commit 072dabd470.

Signed-off-by: Christophe Bedard <bedard.christophe@gmail.com>

* Re-add but deprecate rclcpp/scope_exit.hpp

Signed-off-by: Christophe Bedard <bedard.christophe@gmail.com>
2021-07-26 16:31:42 -07:00
Aditya Pande
e8cbfe6a1b 12.0.0 2021-07-26 14:07:39 -07:00
Aditya Pande
0c01a43a4f Updated CHANGELOG.rst
Signed-off-by: Aditya Pande <aditya050995@gmail.com>
2021-07-26 14:04:29 -07:00
Chris Lalancette
133088e9a3 Remove unsafe get_callback_groups API.
Callers should change to using for_each_callback_group(), or
store the callback groups they need internally.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2021-07-23 16:55:13 -04:00
Chris Lalancette
3d42c9a5df Add in callback_groups_for_each.
The main reason to add this method in is to make accesses to the
callback_groups_ vector thread-safe.  By having a
callback_groups_for_each that accepts a std::function, we can
just have the callers give us the callback they are interested
in, and we can take care of the locking.

The rest of this fairly large PR is cleaning up all of the places
that use get_callback_groups() to instead use
callback_groups_for_each().

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2021-07-23 16:55:13 -04:00
Ivan Santiago Paunovic
5c4f809f2a Use a different mechanism to avoid timers being scheduled multiple times by the MultiThreadedExecutor (#1692)
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
2021-07-23 10:41:26 -03:00
Ivan Santiago Paunovic
f7a301441a Fix windows CI (#1726)
Fix bug in AnyServiceCallback introduced in #1709.

Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
2021-07-22 18:48:08 -03:00
Ivan Santiago Paunovic
00f2d563be 11.2.0 2021-07-21 16:52:06 +00:00
Ivan Santiago Paunovic
64ee7d6822 Changelogs
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
2021-07-21 13:37:47 -03:00
Ivan Santiago Paunovic
0750dc418a Support to defer to send a response in services (#1709)
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
2021-07-21 13:32:38 -03:00
Rebecca Butler
0d6d9e6778 Deprecate method names that use CamelCase in rclcpp_components (#1716)
* Rename methods in ComponentManager to use snake_case

Signed-off-by: Rebecca Butler <rebecca@openrobotics.org>

* Deprecate old method names

Signed-off-by: Rebecca Butler <rebecca@openrobotics.org>
2021-07-19 13:42:12 -04:00
William Woodall
86c079de31 fix documentation bug (#1719)
Signed-off-by: William Woodall <william@osrfoundation.org>
2021-07-16 13:41:34 -07:00
William Woodall
fb8519070c 11.1.0 2021-07-13 14:35:59 -05:00
William Woodall
e1095adeee changelogs
Signed-off-by: William Woodall <william@osrfoundation.org>
2021-07-13 14:35:45 -05:00
William Woodall
4ecb3dd090 remove left over is_initialized() implementation (#1711)
Leftover from https://github.com/ros2/rclcpp/pull/1622

Signed-off-by: William Woodall <william@osrfoundation.org>
2021-07-12 19:19:01 -07:00
Chen Lihui
0034929eef to support declare parameter with int and float vector (#1696)
* to support declare parameter with int vector

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

* update for float vector

Signed-off-by: Chen Lihui <lihui.chen@sony.com>
2021-07-07 08:33:56 -07:00
Rebecca Butler
dbb717cd6e Add hook to generate node options in ComponentManager (#1702)
* Add domain bridge container and `domain` argument for components

Signed-off-by: Rebecca Butler <rebecca@openrobotics.org>

* Fix linter errors

Signed-off-by: Rebecca Butler <rebecca@openrobotics.org>

* Move domain_bridge_container to domain_bridge package

Signed-off-by: Rebecca Butler <rebecca@openrobotics.org>

* Remove domain id argument and add getters/setters to ComponentManager

Signed-off-by: Rebecca Butler <rebecca@openrobotics.org>

* Add SetNodeOptions function

Signed-off-by: Rebecca Butler <rebecca@openrobotics.org>

* Rename SetNodeOptions -> CreateNodeOptions and refactor

Signed-off-by: Rebecca Butler <rebecca@openrobotics.org>
2021-07-07 08:46:20 -04:00
Alberto Soragna
e9e398d2d4 cleanup intra-process-manager (#1695)
Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>
2021-06-23 12:21:26 -07:00
Steve Macenski
7d8b2690ff adding node name to the executor runtime_error (#1686)
* adding node name to the executor runtime_error

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

* changing get_name() to get_fully_qualified_name() + linting

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

* changing print format for tests

Signed-off-by: Steve Macenski <stevenmacenski@gmail.com>
2021-06-06 09:07:10 +09:00
Chris Lalancette
55d2f67b90 Change the word "Attack" -> "Attach". (#1687)
Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2021-06-04 09:48:25 -04:00
Petter Nilsson
39bfb30eb0 Remove use of std::allocator<>::rebind (#1678)
rebind is deprecated in c++17 and removed in c++20

Signed-off-by: Petter Nilsson <pettni@umich.edu>
2021-05-26 19:40:45 -07:00
Kaven Yau
1c61751540 Fix occasionally missing goal result caused by race condition (#1677)
* Fix occasionally missing goal result caused by race condition

Signed-off-by: Kaven Yau <kavenyau@foxmail.com>

* Take action_server_reentrant_mutex_ out of the sending result loop

Signed-off-by: Kaven Yau <kavenyau@foxmail.com>

* add note for explaining the current locking order in server.cpp

Signed-off-by: Kaven Yau <kavenyau@foxmail.com>
2021-05-20 17:15:59 -03:00
Jacob Perron
0251f70fe8 11.0.0 2021-05-18 18:00:56 -07:00
Jacob Perron
07b6ea0ff4 Declare parameters uninitialized (#1673)
* Declare parameters uninitialized

Fixes #1649

Allow declaring parameters without an initial value or override.
This was possible prior to Galactic, but was made impossible since we started enforcing the types of parameters in Galactic.

Signed-off-by: Jacob Perron <jacob@openrobotics.org>

* Remove assertion

Signed-off-by: Jacob Perron <jacob@openrobotics.org>

* Throw NoParameterOverrideProvided exception if accessed before initialized

Signed-off-by: Jacob Perron <jacob@openrobotics.org>

* Add test getting static parameter after it is set

Signed-off-by: Jacob Perron <jacob@openrobotics.org>

* Do not throw on access of uninitialized dynamically typed parameter

Signed-off-by: Jacob Perron <jacob@openrobotics.org>

* Rename exception type

Signed-off-by: Jacob Perron <jacob@openrobotics.org>

* Remove unused exception type

Signed-off-by: Jacob Perron <jacob@openrobotics.org>

* Uncrustify

Signed-off-by: Jacob Perron <jacob@openrobotics.org>
2021-05-18 17:55:48 -07:00
Scott K Logan
56f68f9c44 Fix destruction order in lifecycle benchmark (#1675)
Signed-off-by: Scott K Logan <logans@cottsay.net>
2021-05-14 21:28:55 -07:00
William Woodall
18fcd9138b fix syntax issue with gcc (#1674)
* fix syntax issue with gcc

Signed-off-by: William Woodall <william@osrfoundation.org>

* uncrustify

Signed-off-by: William Woodall <william@osrfoundation.org>
2021-05-14 18:33:10 -07:00
Scott K Logan
f245b4cc81 Bump the benchmark timeout for benchmark_action_client (#1671)
Pausing and resuming the measurement inside the timing loop can cause
the initial run duration calculation to underestimate how long the
benchmark is taking to run, which results in the recorded run taking a
lot longer than it should. This is a known issue in libbenchmark.

This test is affected by that behavior, and typically takes a bit longer
than the others. The easiest thing to do right now is to just bump the
timeout. My tests show that 180 seconds is typically sufficient for this
test, so 240 should be a safe point to conclude that the test is
malfunctioning.

Signed-off-by: Scott K Logan <logans@cottsay.net>
2021-05-13 12:10:19 -07:00
Ivan Santiago Paunovic
d488535f36 [service] Don't use a weak_ptr to avoid leaking (#1668)
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
2021-05-13 12:13:07 -03:00
Chris Lalancette
72443ff295 10.0.0 2021-05-11 15:12:49 +00:00
Chris Lalancette
a3383c309a Changelog.
Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2021-05-11 15:12:40 +00:00
Jacob Perron
5dad229662 Fix doc typo (#1663)
Signed-off-by: Jacob Perron <jacob@openrobotics.org>
2021-05-10 12:45:34 -07:00
William Woodall
2e4c97ac56 [rclcpp] Type Adaptation feature (#1557)
* initial version of type_adaptor.hpp

Signed-off-by: William Woodall <william@osrfoundation.org>

* initial version of rclcpp::get_message_type_support_handle()

Signed-off-by: William Woodall <william@osrfoundation.org>

* initial version of rclcpp::is_ros_compatible_type check

Signed-off-by: William Woodall <william@osrfoundation.org>

* fixup include statement order in publisher.hpp

Signed-off-by: William Woodall <william@osrfoundation.org>

* use new rclcpp::get_message_type_support_handle() and check in Publisher

Signed-off-by: William Woodall <william@osrfoundation.org>

* update adaptor->adapter, update TypeAdapter to use two arguments, add implicit default

Signed-off-by: William Woodall <william@osrfoundation.org>

* move away from shared_ptr<allocator> to just allocator, like the STL

Signed-off-by: William Woodall <william@osrfoundation.org>

* fixes to TypeAdapter and adding new publish function signatures

Signed-off-by: William Woodall <william@osrfoundation.org>

* bugfixes

Signed-off-by: William Woodall <william@osrfoundation.org>

* more bugfixes

Signed-off-by: William Woodall <william@osrfoundation.org>

* Add nullptr check

Signed-off-by: Audrow Nash <audrow@hey.com>

* Remove public from struct inheritance

Signed-off-by: Audrow Nash <audrow@hey.com>

* Add tests for publisher with type adapter

Signed-off-by: Audrow Nash <audrow@hey.com>

* Update packages to C++17

Signed-off-by: Audrow Nash <audrow@hey.com>

* Revert "Update packages to C++17"

This reverts commit 4585605223639bbd9d18053e5ef39725638512b4.

Signed-off-by: William Woodall <william@osrfoundation.org>

* Begin updating AnySubscriptionCallback to use the TypeAdapter

Signed-off-by: Audrow Nash <audrow@hey.com>

* Use type adapter's custom type

Signed-off-by: Audrow Nash <audrow@hey.com>

* Correct which AnySubscriptionCallbackHelper is selected

Signed-off-by: Audrow Nash <audrow@hey.com>

* Setup dispatch function to work with adapted types

Signed-off-by: Audrow Nash <audrow@hey.com>

* Improve template logic on dispatch methods

Signed-off-by: Audrow Nash <audrow@hey.com>

* implement TypeAdapter for Subscription

Signed-off-by: William Woodall <william@osrfoundation.org>

* Add intraprocess tests with all supported message types

Signed-off-by: Audrow Nash <audrow@hey.com>

* Add intra process tests

Signed-off-by: Audrow Nash <audrow@hey.com>

* Add tests for subscription with type adapter

Signed-off-by: Audrow Nash <audrow@hey.com>

* Fix null allocator test

Signed-off-by: Audrow Nash <audrow@hey.com>

* Handle serialized message correctly

Signed-off-by: Audrow Nash <audrow@hey.com>

* Fix generic subscription

Signed-off-by: Audrow Nash <audrow@hey.com>

* Fix trailing space

Signed-off-by: Audrow Nash <audrow@hey.com>

* fix some issues found while testing type_adapter in demos

Signed-off-by: William Woodall <william@osrfoundation.org>

* add more tests, WIP

Signed-off-by: William Woodall <william@osrfoundation.org>

* Improve pub/sub tests

Signed-off-by: Audrow Nash <audrow@hey.com>

* Apply uncrustify formatting

Signed-off-by: Audrow Nash <audrow@hey.com>

* finish new tests for any subscription callback with type adapter

Signed-off-by: William Woodall <william@osrfoundation.org>

* fix adapt_type<...>::as<...> syntax

Signed-off-by: William Woodall <william@osrfoundation.org>

* fix explicit template instantiation of create_subscription() in new test

Signed-off-by: William Woodall <william@osrfoundation.org>

* cpplint fix

Signed-off-by: William Woodall <william@osrfoundation.org>

* Fix bug by aligning allocator types on both sides of ipm

Signed-off-by: Audrow Nash <audrow@hey.com>

* Fix intra process manager tests

Signed-off-by: Audrow Nash <audrow@hey.com>

Co-authored-by: Audrow Nash <audrow@hey.com>
2021-05-06 21:33:06 -07:00
Michel Hidalgo
0659d829ce Do not attempt to use void allocators for memory allocation. (#1657)
Keep a rebound allocator for byte-sized memory blocks around
for publisher and subscription options.

Follow-up after 1fc2d58799

Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>
2021-05-05 13:17:42 -07:00
Michel Hidalgo
1fc2d58799 Keep custom allocator in publisher and subscription options alive. (#1647)
* Keep custom allocator in publisher and subscription options alive.

Also, enforce an allocator bound to void is used to avoid surprises.

Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>

* Avoid sizeof(void) in MyAllocator implementation.

Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>

* Address peer review comment

Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>

* Use a lazely initialized private field when 'allocator' is not initialized

Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>

Co-authored-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
2021-05-04 15:45:15 -07:00
Michel Hidalgo
c15eb1eaac Fix get_publishers_subscriptions_info_by_topic test in test_node.cpp (#1648)
* Wait on graph guard condition for graph changes to propagate

Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>
2021-05-03 15:46:25 -03:00
Kaven Yau
d051b8aa20 Returns CancelResponse::REJECT while goal handle failed to transit to CANCELING state (#1641)
Signed-off-by: Kaven Yau <kavenyau@foxmail.com>
2021-04-30 17:56:38 -03:00
Barry Xu
6806cdf825 Use OnShutdown callback handle instead of OnShutdown callback (#1639)
1. Add remove_on_shutdown_callback() in rclcpp::Context

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

2. Add add_on_shutdown_callback(), which returns a handle that can be removed by remove_on_shutdown_callback().

Signed-off-by: Barry Xu <barry.xu@sony.com>
2021-04-30 14:53:19 -03:00
William Woodall
79c2dd8e8b use dynamic_pointer_cast to detect allocator mismatch in intra process manager (#1643)
* use dynamic_pointer_cast to detect allocator mismatch in intra process manager

Signed-off-by: William Woodall <william@osrfoundation.org>

* add test case for mismatched allocators

Signed-off-by: William Woodall <william@osrfoundation.org>

* forward template arguments to avoid mismatched types in intra process manager

Signed-off-by: William Woodall <william@osrfoundation.org>

* style fixes

Signed-off-by: William Woodall <william@osrfoundation.org>

* refactor to test message address and count, more DRY

Signed-off-by: William Woodall <william@osrfoundation.org>

* update copyright

Signed-off-by: William Woodall <william@osrfoundation.org>

* fix typo

Signed-off-by: William Woodall <william@osrfoundation.org>

Co-authored-by: Michel Hidalgo <michel@ekumenlabs.com>

Co-authored-by: Michel Hidalgo <michel@ekumenlabs.com>
2021-04-29 10:56:16 -07:00
Kaven Yau
fba080cf34 Fix action server deadlock issue that caused by other mutexes locked in CancelCallback (#1635)
* Fix deadlock issue that caused by other mutexes locked in CancelCallback

Signed-off-by: Kaven Yau <love29881460@qq.com>

* Add unit test for rclcpp action server deadlock

Signed-off-by: Kaven Yau <love29881460@qq.com>

* Update rclcpp_action/test/test_server.cpp

Co-authored-by: William Woodall <william+github@osrfoundation.org>

Co-authored-by: Kaven Yau <love29881460@qq.com>
Co-authored-by: Jacob Perron <jacob@openrobotics.org>
Co-authored-by: William Woodall <william+github@osrfoundation.org>
2021-04-25 16:32:30 +09:00
103 changed files with 5271 additions and 1406 deletions

View File

@@ -2,6 +2,79 @@
Changelog for package rclcpp
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
13.0.0 (2021-08-23)
-------------------
* Remove can_be_nullptr assignment check for QNX case. (`#1752 <https://github.com/ros2/rclcpp/issues/1752>`_)
* Update client API to be able to remove pending requests. (`#1734 <https://github.com/ros2/rclcpp/issues/1734>`_)
* Fix: Allow to add a node while spinning in the StaticSingleThreadedExecutor. (`#1690 <https://github.com/ros2/rclcpp/issues/1690>`_)
* Add tracing instrumentation for executor and message taking. (`#1738 <https://github.com/ros2/rclcpp/issues/1738>`_)
* Fix: Reset timer trigger time before execute in StaticSingleThreadedExecutor. (`#1739 <https://github.com/ros2/rclcpp/issues/1739>`_)
* Use FindPython3 and make python3 dependency explicit. (`#1745 <https://github.com/ros2/rclcpp/issues/1745>`_)
* Use rosidl_get_typesupport_target(). (`#1729 <https://github.com/ros2/rclcpp/issues/1729>`_)
* Fix returning invalid namespace if sub_namespace is empty. (`#1658 <https://github.com/ros2/rclcpp/issues/1658>`_)
* Add free function to wait for a subscription message. (`#1705 <https://github.com/ros2/rclcpp/issues/1705>`_)
* Use rcpputils/scope_exit.hpp and remove rclcpp/scope_exit.hpp. (`#1727 <https://github.com/ros2/rclcpp/issues/1727>`_)
* Contributors: Ahmed Sobhy, Christophe Bedard, Ivan Santiago Paunovic, Karsten Knese, M. Hofstätter, Mauro Passerino, Shane Loretz, mauropasse
12.0.0 (2021-07-26)
-------------------
* Remove unsafe get_callback_groups API.
Callers should change to using for_each_callback_group(), or
store the callback groups they need internally.
* Add in callback_groups_for_each.
The main reason to add this method in is to make accesses to the
callback_groups\_ vector thread-safe. By having a
callback_groups_for_each that accepts a std::function, we can
just have the callers give us the callback they are interested
in, and we can take care of the locking.
The rest of this fairly large PR is cleaning up all of the places
that use get_callback_groups() to instead use
callback_groups_for_each().
* Use a different mechanism to avoid timers being scheduled multiple times by the MultiThreadedExecutor (`#1692 <https://github.com/ros2/rclcpp/issues/1692>`_)
* Fix windows CI (`#1726 <https://github.com/ros2/rclcpp/issues/1726>`_)
Fix bug in AnyServiceCallback introduced in `#1709 <https://github.com/ros2/rclcpp/issues/1709>`_.
* Contributors: Chris Lalancette, Ivan Santiago Paunovic
11.2.0 (2021-07-21)
-------------------
* Support to defer to send a response in services. (`#1709 <https://github.com/ros2/rclcpp/issues/1709>`_)
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
* Fix documentation bug. (`#1719 <https://github.com/ros2/rclcpp/issues/1719>`_)
Signed-off-by: William Woodall <william@osrfoundation.org>
* Contributors: Ivan Santiago Paunovic, William Woodall
11.1.0 (2021-07-13)
-------------------
* Removed left over ``is_initialized()`` implementation (`#1711 <https://github.com/ros2/rclcpp/issues/1711>`_)
Leftover from https://github.com/ros2/rclcpp/pull/1622
* Fixed declare parameter methods for int and float vectors (`#1696 <https://github.com/ros2/rclcpp/issues/1696>`_)
* Cleaned up implementation of the intra-process manager (`#1695 <https://github.com/ros2/rclcpp/issues/1695>`_)
* Added the node name to an executor ``runtime_error`` (`#1686 <https://github.com/ros2/rclcpp/issues/1686>`_)
* Fixed a typo "Attack" -> "Attach" (`#1687 <https://github.com/ros2/rclcpp/issues/1687>`_)
* Removed use of std::allocator<>::rebind (`#1678 <https://github.com/ros2/rclcpp/issues/1678>`_)
rebind is deprecated in c++17 and removed in c++20
* Contributors: Alberto Soragna, Chen Lihui, Chris Lalancette, Petter Nilsson, Steve Macenski, William Woodall
11.0.0 (2021-05-18)
-------------------
* Allow declare uninitialized parameters (`#1673 <https://github.com/ros2/rclcpp/issues/1673>`_)
* Fix syntax issue with gcc (`#1674 <https://github.com/ros2/rclcpp/issues/1674>`_)
* [service] Don't use a weak_ptr to avoid leaking (`#1668 <https://github.com/ros2/rclcpp/issues/1668>`_)
* Contributors: Ivan Santiago Paunovic, Jacob Perron, William Woodall
10.0.0 (2021-05-11)
-------------------
* Fix doc typo (`#1663 <https://github.com/ros2/rclcpp/issues/1663>`_)
* [rclcpp] Type Adaptation feature (`#1557 <https://github.com/ros2/rclcpp/issues/1557>`_)
* Do not attempt to use void allocators for memory allocation. (`#1657 <https://github.com/ros2/rclcpp/issues/1657>`_)
* Keep custom allocator in publisher and subscription options alive. (`#1647 <https://github.com/ros2/rclcpp/issues/1647>`_)
* Fix get_publishers_subscriptions_info_by_topic test in test_node.cpp (`#1648 <https://github.com/ros2/rclcpp/issues/1648>`_)
* Use OnShutdown callback handle instead of OnShutdown callback (`#1639 <https://github.com/ros2/rclcpp/issues/1639>`_)
* use dynamic_pointer_cast to detect allocator mismatch in intra process manager (`#1643 <https://github.com/ros2/rclcpp/issues/1643>`_)
* Increase cppcheck timeout to 500s (`#1634 <https://github.com/ros2/rclcpp/issues/1634>`_)
* Clarify node parameters docs (`#1631 <https://github.com/ros2/rclcpp/issues/1631>`_)
* Contributors: Audrow Nash, Barry Xu, Jacob Perron, Michel Hidalgo, Shane Loretz, William Woodall
9.0.2 (2021-04-14)
------------------
* Avoid returning loan when none was obtained. (`#1629 <https://github.com/ros2/rclcpp/issues/1629>`_)

View File

@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.5)
cmake_minimum_required(VERSION 3.12)
project(rclcpp)
@@ -41,7 +41,6 @@ set(${PROJECT_NAME}_SRCS
src/rclcpp/clock.cpp
src/rclcpp/context.cpp
src/rclcpp/contexts/default_context.cpp
src/rclcpp/detail/mutex_two_priorities.cpp
src/rclcpp/detail/resolve_parameter_overrides.cpp
src/rclcpp/detail/rmw_implementation_specific_payload.cpp
src/rclcpp/detail/rmw_implementation_specific_publisher_payload.cpp
@@ -110,6 +109,8 @@ set(${PROJECT_NAME}_SRCS
src/rclcpp/waitable.cpp
)
find_package(Python3 REQUIRED COMPONENTS Interpreter)
# "watch" template for changes
configure_file(
"resource/logging.hpp.em"
@@ -123,7 +124,7 @@ set(python_code_logging
string(REPLACE ";" "$<SEMICOLON>" python_code_logging "${python_code_logging}")
add_custom_command(OUTPUT include/rclcpp/logging.hpp
COMMAND ${CMAKE_COMMAND} -E make_directory "include/rclcpp"
COMMAND ${PYTHON_EXECUTABLE} ARGS -c "${python_code_logging}"
COMMAND Python3::Interpreter ARGS -c "${python_code_logging}"
DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/logging.hpp.em.watch"
COMMENT "Expanding logging.hpp.em"
VERBATIM
@@ -147,7 +148,7 @@ foreach(interface_file ${interface_files})
string(REPLACE ";" "$<SEMICOLON>" python_${interface_name}_traits "${python_${interface_name}_traits}")
add_custom_command(OUTPUT include/rclcpp/node_interfaces/${interface_name}_traits.hpp
COMMAND ${CMAKE_COMMAND} -E make_directory "include/rclcpp/node_interfaces"
COMMAND ${PYTHON_EXECUTABLE} ARGS -c "${python_${interface_name}_traits}"
COMMAND Python3::Interpreter ARGS -c "${python_${interface_name}_traits}"
DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${interface_name}_traits.hpp.em.watch"
COMMENT "Expanding interface_traits.hpp.em into ${interface_name}_traits.hpp"
VERBATIM
@@ -167,7 +168,7 @@ foreach(interface_file ${interface_files})
string(REPLACE ";" "$<SEMICOLON>" python_get_${interface_name} "${python_get_${interface_name}}")
add_custom_command(OUTPUT include/rclcpp/node_interfaces/get_${interface_name}.hpp
COMMAND ${CMAKE_COMMAND} -E make_directory "include/rclcpp/node_interfaces"
COMMAND ${PYTHON_EXECUTABLE} ARGS -c "${python_get_${interface_name}}"
COMMAND Python3::Interpreter ARGS -c "${python_get_${interface_name}}"
DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/get_${interface_name}.hpp.em.watch"
COMMENT "Expanding get_interface.hpp.em into get_${interface_file}.hpp"
VERBATIM

View File

@@ -95,7 +95,7 @@ void set_allocator_for_deleter(AllocatorDeleter<T> * deleter, Alloc * alloc)
template<typename Alloc, typename T>
using Deleter = typename std::conditional<
std::is_same<typename std::allocator_traits<Alloc>::template rebind_alloc<T>,
typename std::allocator<void>::template rebind<T>::other>::value,
std::allocator<T>>::value,
std::default_delete<T>,
AllocatorDeleter<Alloc>
>::type;

View File

@@ -15,10 +15,12 @@
#ifndef RCLCPP__ANY_SERVICE_CALLBACK_HPP_
#define RCLCPP__ANY_SERVICE_CALLBACK_HPP_
#include <variant>
#include <functional>
#include <memory>
#include <stdexcept>
#include <type_traits>
#include <utility>
#include "rclcpp/function_traits.hpp"
#include "rclcpp/visibility_control.hpp"
@@ -29,93 +31,204 @@
namespace rclcpp
{
namespace detail
{
template<typename T, typename = void>
struct can_be_nullptr : std::false_type {};
// Some lambdas define a comparison with nullptr,
// but we see a warning that they can never be null when using it.
// We also test if `T &` can be assigned to `nullptr` to avoid the issue.
template<typename T>
#ifdef __QNXNTO__
struct can_be_nullptr<T, std::void_t<
decltype(std::declval<T>() == nullptr)>>: std::true_type {};
#else
struct can_be_nullptr<T, std::void_t<
decltype(std::declval<T>() == nullptr), decltype(std::declval<T &>() = nullptr)>>
: std::true_type {};
#endif
} // namespace detail
// Forward declare
template<typename ServiceT>
class Service;
template<typename ServiceT>
class AnyServiceCallback
{
private:
using SharedPtrCallback = std::function<
void (
const std::shared_ptr<typename ServiceT::Request>,
std::shared_ptr<typename ServiceT::Response>
)>;
using SharedPtrWithRequestHeaderCallback = std::function<
void (
const std::shared_ptr<rmw_request_id_t>,
const std::shared_ptr<typename ServiceT::Request>,
std::shared_ptr<typename ServiceT::Response>
)>;
SharedPtrCallback shared_ptr_callback_;
SharedPtrWithRequestHeaderCallback shared_ptr_with_request_header_callback_;
public:
AnyServiceCallback()
: shared_ptr_callback_(nullptr), shared_ptr_with_request_header_callback_(nullptr)
: callback_(std::monostate{})
{}
AnyServiceCallback(const AnyServiceCallback &) = default;
template<
typename CallbackT,
typename std::enable_if<
typename std::enable_if_t<!detail::can_be_nullptr<CallbackT>::value, int> = 0>
void
set(CallbackT && callback)
{
// Workaround Windows issue with std::bind
if constexpr (
rclcpp::function_traits::same_arguments<
CallbackT,
SharedPtrCallback
>::value
>::type * = nullptr
>
void set(CallbackT callback)
{
shared_ptr_callback_ = callback;
>::value)
{
callback_.template emplace<SharedPtrCallback>(callback);
} else if constexpr ( // NOLINT, can't satisfy both cpplint and uncrustify
rclcpp::function_traits::same_arguments<
CallbackT,
SharedPtrWithRequestHeaderCallback
>::value)
{
callback_.template emplace<SharedPtrWithRequestHeaderCallback>(callback);
} else if constexpr ( // NOLINT
rclcpp::function_traits::same_arguments<
CallbackT,
SharedPtrDeferResponseCallback
>::value)
{
callback_.template emplace<SharedPtrDeferResponseCallback>(callback);
} else if constexpr ( // NOLINT
rclcpp::function_traits::same_arguments<
CallbackT,
SharedPtrDeferResponseCallbackWithServiceHandle
>::value)
{
callback_.template emplace<SharedPtrDeferResponseCallbackWithServiceHandle>(callback);
} else {
// the else clause is not needed, but anyways we should only be doing this instead
// of all the above workaround ...
callback_ = std::forward<CallbackT>(callback);
}
}
template<
typename CallbackT,
typename std::enable_if<
typename std::enable_if_t<detail::can_be_nullptr<CallbackT>::value, int> = 0>
void
set(CallbackT && callback)
{
if (!callback) {
throw std::invalid_argument("AnyServiceCallback::set(): callback cannot be nullptr");
}
// Workaround Windows issue with std::bind
if constexpr (
rclcpp::function_traits::same_arguments<
CallbackT,
SharedPtrCallback
>::value)
{
callback_.template emplace<SharedPtrCallback>(callback);
} else if constexpr ( // NOLINT
rclcpp::function_traits::same_arguments<
CallbackT,
SharedPtrWithRequestHeaderCallback
>::value
>::type * = nullptr
>
void set(CallbackT callback)
{
shared_ptr_with_request_header_callback_ = callback;
>::value)
{
callback_.template emplace<SharedPtrWithRequestHeaderCallback>(callback);
} else if constexpr ( // NOLINT
rclcpp::function_traits::same_arguments<
CallbackT,
SharedPtrDeferResponseCallback
>::value)
{
callback_.template emplace<SharedPtrDeferResponseCallback>(callback);
} else if constexpr ( // NOLINT
rclcpp::function_traits::same_arguments<
CallbackT,
SharedPtrDeferResponseCallbackWithServiceHandle
>::value)
{
callback_.template emplace<SharedPtrDeferResponseCallbackWithServiceHandle>(callback);
} else {
// the else clause is not needed, but anyways we should only be doing this instead
// of all the above workaround ...
callback_ = std::forward<CallbackT>(callback);
}
}
void dispatch(
std::shared_ptr<rmw_request_id_t> request_header,
std::shared_ptr<typename ServiceT::Request> request,
std::shared_ptr<typename ServiceT::Response> response)
// template<typename Allocator = std::allocator<typename ServiceT::Response>>
std::shared_ptr<typename ServiceT::Response>
dispatch(
const std::shared_ptr<rclcpp::Service<ServiceT>> & service_handle,
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);
if (shared_ptr_callback_ != nullptr) {
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.
throw std::runtime_error{"unexpected request without any callback set"};
}
if (std::holds_alternative<SharedPtrDeferResponseCallback>(callback_)) {
const auto & cb = std::get<SharedPtrDeferResponseCallback>(callback_);
cb(request_header, std::move(request));
return nullptr;
}
if (std::holds_alternative<SharedPtrDeferResponseCallbackWithServiceHandle>(callback_)) {
const auto & cb = std::get<SharedPtrDeferResponseCallbackWithServiceHandle>(callback_);
cb(service_handle, request_header, std::move(request));
return nullptr;
}
// auto response = allocate_shared<typename ServiceT::Response, Allocator>();
auto response = std::make_shared<typename ServiceT::Response>();
if (std::holds_alternative<SharedPtrCallback>(callback_)) {
(void)request_header;
shared_ptr_callback_(request, response);
} else if (shared_ptr_with_request_header_callback_ != nullptr) {
shared_ptr_with_request_header_callback_(request_header, request, response);
} else {
throw std::runtime_error("unexpected request without any callback set");
const auto & cb = std::get<SharedPtrCallback>(callback_);
cb(std::move(request), response);
} else if (std::holds_alternative<SharedPtrWithRequestHeaderCallback>(callback_)) {
const auto & cb = std::get<SharedPtrWithRequestHeaderCallback>(callback_);
cb(request_header, std::move(request), response);
}
TRACEPOINT(callback_end, static_cast<const void *>(this));
return response;
}
void register_callback_for_tracing()
{
#ifndef TRACETOOLS_DISABLED
if (shared_ptr_callback_) {
TRACEPOINT(
rclcpp_callback_register,
static_cast<const void *>(this),
tracetools::get_symbol(shared_ptr_callback_));
} else if (shared_ptr_with_request_header_callback_) {
TRACEPOINT(
rclcpp_callback_register,
static_cast<const void *>(this),
tracetools::get_symbol(shared_ptr_with_request_header_callback_));
}
std::visit(
[this](auto && arg) {
TRACEPOINT(
rclcpp_callback_register,
static_cast<const void *>(this),
tracetools::get_symbol(arg));
}, callback_);
#endif // TRACETOOLS_DISABLED
}
private:
using SharedPtrCallback = std::function<
void (
std::shared_ptr<typename ServiceT::Request>,
std::shared_ptr<typename ServiceT::Response>
)>;
using SharedPtrWithRequestHeaderCallback = std::function<
void (
std::shared_ptr<rmw_request_id_t>,
std::shared_ptr<typename ServiceT::Request>,
std::shared_ptr<typename ServiceT::Response>
)>;
using SharedPtrDeferResponseCallback = std::function<
void (
std::shared_ptr<rmw_request_id_t>,
std::shared_ptr<typename ServiceT::Request>
)>;
using SharedPtrDeferResponseCallbackWithServiceHandle = std::function<
void (
std::shared_ptr<rclcpp::Service<ServiceT>>,
std::shared_ptr<rmw_request_id_t>,
std::shared_ptr<typename ServiceT::Request>
)>;
std::variant<
std::monostate,
SharedPtrCallback,
SharedPtrWithRequestHeaderCallback,
SharedPtrDeferResponseCallback,
SharedPtrDeferResponseCallbackWithServiceHandle> callback_;
};
} // namespace rclcpp

View File

@@ -22,6 +22,7 @@
#include <utility>
#include <variant> // NOLINT[build/include_order]
#include "rosidl_runtime_cpp/traits.hpp"
#include "tracetools/tracetools.h"
#include "tracetools/utils.hpp"
@@ -29,6 +30,9 @@
#include "rclcpp/detail/subscription_callback_type_helper.hpp"
#include "rclcpp/function_traits.hpp"
#include "rclcpp/message_info.hpp"
#include "rclcpp/serialized_message.hpp"
#include "rclcpp/type_adapter.hpp"
template<class>
inline constexpr bool always_false_v = false;
@@ -42,53 +46,189 @@ namespace detail
template<typename MessageT, typename AllocatorT>
struct MessageDeleterHelper
{
using MessageAllocTraits = allocator::AllocRebind<MessageT, AllocatorT>;
using MessageAlloc = typename MessageAllocTraits::allocator_type;
using MessageDeleter = allocator::Deleter<MessageAlloc, MessageT>;
using AllocTraits = allocator::AllocRebind<MessageT, AllocatorT>;
using Alloc = typename AllocTraits::allocator_type;
using Deleter = allocator::Deleter<Alloc, MessageT>;
};
/// Struct which contains all possible callback signatures, with or without a TypeAdapter.s
template<typename MessageT, typename AllocatorT>
struct AnySubscriptionCallbackHelper
struct AnySubscriptionCallbackPossibleTypes
{
using MessageDeleter = typename MessageDeleterHelper<MessageT, AllocatorT>::MessageDeleter;
/// MessageT::custom_type if MessageT is a TypeAdapter, otherwise just MessageT.
using SubscribedType = typename rclcpp::TypeAdapter<MessageT>::custom_type;
/// MessageT::ros_message_type if MessageT is a TypeAdapter, otherwise just MessageT.
using ROSMessageType = typename rclcpp::TypeAdapter<MessageT>::ros_message_type;
using SubscribedMessageDeleter =
typename MessageDeleterHelper<SubscribedType, AllocatorT>::Deleter;
using ROSMessageDeleter =
typename MessageDeleterHelper<ROSMessageType, AllocatorT>::Deleter;
using SerializedMessageDeleter =
typename MessageDeleterHelper<rclcpp::SerializedMessage, AllocatorT>::Deleter;
using ConstRefCallback =
std::function<void (const MessageT &)>;
std::function<void (const SubscribedType &)>;
using ConstRefROSMessageCallback =
std::function<void (const ROSMessageType &)>;
using ConstRefWithInfoCallback =
std::function<void (const MessageT &, const rclcpp::MessageInfo &)>;
std::function<void (const SubscribedType &, const rclcpp::MessageInfo &)>;
using ConstRefWithInfoROSMessageCallback =
std::function<void (const ROSMessageType &, const rclcpp::MessageInfo &)>;
using ConstRefSerializedMessageCallback =
std::function<void (const rclcpp::SerializedMessage &)>;
using ConstRefSerializedMessageWithInfoCallback =
std::function<void (const rclcpp::SerializedMessage &, const rclcpp::MessageInfo &)>;
using UniquePtrCallback =
std::function<void (std::unique_ptr<MessageT, MessageDeleter>)>;
std::function<void (std::unique_ptr<SubscribedType, SubscribedMessageDeleter>)>;
using UniquePtrROSMessageCallback =
std::function<void (std::unique_ptr<ROSMessageType, ROSMessageDeleter>)>;
using UniquePtrWithInfoCallback =
std::function<void (std::unique_ptr<MessageT, MessageDeleter>, const rclcpp::MessageInfo &)>;
std::function<void (
std::unique_ptr<SubscribedType, SubscribedMessageDeleter>,
const rclcpp::MessageInfo &)>;
using UniquePtrWithInfoROSMessageCallback =
std::function<void (
std::unique_ptr<ROSMessageType, ROSMessageDeleter>,
const rclcpp::MessageInfo &)>;
using UniquePtrSerializedMessageCallback =
std::function<void (std::unique_ptr<rclcpp::SerializedMessage, SerializedMessageDeleter>)>;
using UniquePtrSerializedMessageWithInfoCallback =
std::function<void (
std::unique_ptr<rclcpp::SerializedMessage, SerializedMessageDeleter>,
const rclcpp::MessageInfo &)>;
using SharedConstPtrCallback =
std::function<void (std::shared_ptr<const MessageT>)>;
std::function<void (std::shared_ptr<const SubscribedType>)>;
using SharedConstPtrROSMessageCallback =
std::function<void (std::shared_ptr<const ROSMessageType>)>;
using SharedConstPtrWithInfoCallback =
std::function<void (std::shared_ptr<const MessageT>, const rclcpp::MessageInfo &)>;
std::function<void (
std::shared_ptr<const SubscribedType>,
const rclcpp::MessageInfo &)>;
using SharedConstPtrWithInfoROSMessageCallback =
std::function<void (
std::shared_ptr<const ROSMessageType>,
const rclcpp::MessageInfo &)>;
using SharedConstPtrSerializedMessageCallback =
std::function<void (std::shared_ptr<const rclcpp::SerializedMessage>)>;
using SharedConstPtrSerializedMessageWithInfoCallback =
std::function<void (
std::shared_ptr<const rclcpp::SerializedMessage>,
const rclcpp::MessageInfo &)>;
using ConstRefSharedConstPtrCallback =
std::function<void (const std::shared_ptr<const MessageT> &)>;
std::function<void (const std::shared_ptr<const SubscribedType> &)>;
using ConstRefSharedConstPtrROSMessageCallback =
std::function<void (const std::shared_ptr<const ROSMessageType> &)>;
using ConstRefSharedConstPtrWithInfoCallback =
std::function<void (const std::shared_ptr<const MessageT> &, const rclcpp::MessageInfo &)>;
std::function<void (
const std::shared_ptr<const SubscribedType> &,
const rclcpp::MessageInfo &)>;
using ConstRefSharedConstPtrWithInfoROSMessageCallback =
std::function<void (
const std::shared_ptr<const ROSMessageType> &,
const rclcpp::MessageInfo &)>;
using ConstRefSharedConstPtrSerializedMessageCallback =
std::function<void (const std::shared_ptr<const rclcpp::SerializedMessage> &)>;
using ConstRefSharedConstPtrSerializedMessageWithInfoCallback =
std::function<void (
const std::shared_ptr<const rclcpp::SerializedMessage> &,
const rclcpp::MessageInfo &)>;
// Deprecated signatures:
using SharedPtrCallback =
std::function<void (std::shared_ptr<MessageT>)>;
std::function<void (std::shared_ptr<SubscribedType>)>;
using SharedPtrROSMessageCallback =
std::function<void (std::shared_ptr<ROSMessageType>)>;
using SharedPtrWithInfoCallback =
std::function<void (std::shared_ptr<MessageT>, const rclcpp::MessageInfo &)>;
std::function<void (std::shared_ptr<SubscribedType>, const rclcpp::MessageInfo &)>;
using SharedPtrWithInfoROSMessageCallback =
std::function<void (
std::shared_ptr<ROSMessageType>,
const rclcpp::MessageInfo &)>;
using SharedPtrSerializedMessageCallback =
std::function<void (std::shared_ptr<rclcpp::SerializedMessage>)>;
using SharedPtrSerializedMessageWithInfoCallback =
std::function<void (std::shared_ptr<rclcpp::SerializedMessage>, const rclcpp::MessageInfo &)>;
};
/// Template helper to select the variant type based on whether or not MessageT is a TypeAdapter.
template<
typename MessageT,
typename AllocatorT,
bool is_adapted_type = rclcpp::TypeAdapter<MessageT>::is_specialized::value
>
struct AnySubscriptionCallbackHelper;
/// Specialization for when MessageT is not a TypeAdapter.
template<typename MessageT, typename AllocatorT>
struct AnySubscriptionCallbackHelper<MessageT, AllocatorT, false>
{
using CallbackTypes = AnySubscriptionCallbackPossibleTypes<MessageT, AllocatorT>;
using variant_type = std::variant<
ConstRefCallback,
ConstRefWithInfoCallback,
UniquePtrCallback,
UniquePtrWithInfoCallback,
SharedConstPtrCallback,
SharedConstPtrWithInfoCallback,
ConstRefSharedConstPtrCallback,
ConstRefSharedConstPtrWithInfoCallback,
SharedPtrCallback,
SharedPtrWithInfoCallback
typename CallbackTypes::ConstRefCallback,
typename CallbackTypes::ConstRefWithInfoCallback,
typename CallbackTypes::ConstRefSerializedMessageCallback,
typename CallbackTypes::ConstRefSerializedMessageWithInfoCallback,
typename CallbackTypes::UniquePtrCallback,
typename CallbackTypes::UniquePtrWithInfoCallback,
typename CallbackTypes::UniquePtrSerializedMessageCallback,
typename CallbackTypes::UniquePtrSerializedMessageWithInfoCallback,
typename CallbackTypes::SharedConstPtrCallback,
typename CallbackTypes::SharedConstPtrWithInfoCallback,
typename CallbackTypes::SharedConstPtrSerializedMessageCallback,
typename CallbackTypes::SharedConstPtrSerializedMessageWithInfoCallback,
typename CallbackTypes::ConstRefSharedConstPtrCallback,
typename CallbackTypes::ConstRefSharedConstPtrWithInfoCallback,
typename CallbackTypes::ConstRefSharedConstPtrSerializedMessageCallback,
typename CallbackTypes::ConstRefSharedConstPtrSerializedMessageWithInfoCallback,
typename CallbackTypes::SharedPtrCallback,
typename CallbackTypes::SharedPtrWithInfoCallback,
typename CallbackTypes::SharedPtrSerializedMessageCallback,
typename CallbackTypes::SharedPtrSerializedMessageWithInfoCallback
>;
};
/// Specialization for when MessageT is a TypeAdapter.
template<typename MessageT, typename AllocatorT>
struct AnySubscriptionCallbackHelper<MessageT, AllocatorT, true>
{
using CallbackTypes = AnySubscriptionCallbackPossibleTypes<MessageT, AllocatorT>;
using variant_type = std::variant<
typename CallbackTypes::ConstRefCallback,
typename CallbackTypes::ConstRefROSMessageCallback,
typename CallbackTypes::ConstRefWithInfoCallback,
typename CallbackTypes::ConstRefWithInfoROSMessageCallback,
typename CallbackTypes::ConstRefSerializedMessageCallback,
typename CallbackTypes::ConstRefSerializedMessageWithInfoCallback,
typename CallbackTypes::UniquePtrCallback,
typename CallbackTypes::UniquePtrROSMessageCallback,
typename CallbackTypes::UniquePtrWithInfoCallback,
typename CallbackTypes::UniquePtrWithInfoROSMessageCallback,
typename CallbackTypes::UniquePtrSerializedMessageCallback,
typename CallbackTypes::UniquePtrSerializedMessageWithInfoCallback,
typename CallbackTypes::SharedConstPtrCallback,
typename CallbackTypes::SharedConstPtrROSMessageCallback,
typename CallbackTypes::SharedConstPtrWithInfoCallback,
typename CallbackTypes::SharedConstPtrWithInfoROSMessageCallback,
typename CallbackTypes::SharedConstPtrSerializedMessageCallback,
typename CallbackTypes::SharedConstPtrSerializedMessageWithInfoCallback,
typename CallbackTypes::ConstRefSharedConstPtrCallback,
typename CallbackTypes::ConstRefSharedConstPtrROSMessageCallback,
typename CallbackTypes::ConstRefSharedConstPtrWithInfoCallback,
typename CallbackTypes::ConstRefSharedConstPtrWithInfoROSMessageCallback,
typename CallbackTypes::ConstRefSharedConstPtrSerializedMessageCallback,
typename CallbackTypes::ConstRefSharedConstPtrSerializedMessageWithInfoCallback,
typename CallbackTypes::SharedPtrCallback,
typename CallbackTypes::SharedPtrROSMessageCallback,
typename CallbackTypes::SharedPtrWithInfoCallback,
typename CallbackTypes::SharedPtrWithInfoROSMessageCallback,
typename CallbackTypes::SharedPtrSerializedMessageCallback,
typename CallbackTypes::SharedPtrSerializedMessageWithInfoCallback
>;
};
@@ -101,49 +241,124 @@ template<
class AnySubscriptionCallback
{
private:
/// MessageT::custom_type if MessageT is a TypeAdapter, otherwise just MessageT.
using SubscribedType = typename rclcpp::TypeAdapter<MessageT>::custom_type;
/// MessageT::ros_message_type if MessageT is a TypeAdapter, otherwise just MessageT.
using ROSMessageType = typename rclcpp::TypeAdapter<MessageT>::ros_message_type;
using HelperT = typename rclcpp::detail::AnySubscriptionCallbackHelper<MessageT, AllocatorT>;
using MessageDeleterHelper = rclcpp::detail::MessageDeleterHelper<MessageT, AllocatorT>;
using MessageAllocTraits = typename MessageDeleterHelper::MessageAllocTraits;
using MessageAlloc = typename MessageDeleterHelper::MessageAlloc;
using MessageDeleter = typename MessageDeleterHelper::MessageDeleter;
using SubscribedTypeDeleterHelper =
rclcpp::detail::MessageDeleterHelper<SubscribedType, AllocatorT>;
using SubscribedTypeAllocatorTraits = typename SubscribedTypeDeleterHelper::AllocTraits;
using SubscribedTypeAllocator = typename SubscribedTypeDeleterHelper::Alloc;
using SubscribedTypeDeleter = typename SubscribedTypeDeleterHelper::Deleter;
// See AnySubscriptionCallbackHelper for the types of these.
using ConstRefCallback = typename HelperT::ConstRefCallback;
using ConstRefWithInfoCallback = typename HelperT::ConstRefWithInfoCallback;
using ROSMessageTypeDeleterHelper =
rclcpp::detail::MessageDeleterHelper<ROSMessageType, AllocatorT>;
using ROSMessageTypeAllocatorTraits = typename ROSMessageTypeDeleterHelper::AllocTraits;
using ROSMessageTypeAllocator = typename ROSMessageTypeDeleterHelper::Alloc;
using ROSMessageTypeDeleter = typename ROSMessageTypeDeleterHelper::Deleter;
using UniquePtrCallback = typename HelperT::UniquePtrCallback;
using UniquePtrWithInfoCallback = typename HelperT::UniquePtrWithInfoCallback;
using SerializedMessageDeleterHelper =
rclcpp::detail::MessageDeleterHelper<rclcpp::SerializedMessage, AllocatorT>;
using SerializedMessageAllocatorTraits = typename SerializedMessageDeleterHelper::AllocTraits;
using SerializedMessageAllocator = typename SerializedMessageDeleterHelper::Alloc;
using SerializedMessageDeleter = typename SerializedMessageDeleterHelper::Deleter;
using SharedConstPtrCallback = typename HelperT::SharedConstPtrCallback;
using SharedConstPtrWithInfoCallback = typename HelperT::SharedConstPtrWithInfoCallback;
// See AnySubscriptionCallbackPossibleTypes for the types of these.
using CallbackTypes = detail::AnySubscriptionCallbackPossibleTypes<MessageT, AllocatorT>;
using ConstRefCallback =
typename CallbackTypes::ConstRefCallback;
using ConstRefROSMessageCallback =
typename CallbackTypes::ConstRefROSMessageCallback;
using ConstRefWithInfoCallback =
typename CallbackTypes::ConstRefWithInfoCallback;
using ConstRefWithInfoROSMessageCallback =
typename CallbackTypes::ConstRefWithInfoROSMessageCallback;
using ConstRefSerializedMessageCallback =
typename CallbackTypes::ConstRefSerializedMessageCallback;
using ConstRefSerializedMessageWithInfoCallback =
typename CallbackTypes::ConstRefSerializedMessageWithInfoCallback;
using UniquePtrCallback =
typename CallbackTypes::UniquePtrCallback;
using UniquePtrROSMessageCallback =
typename CallbackTypes::UniquePtrROSMessageCallback;
using UniquePtrWithInfoCallback =
typename CallbackTypes::UniquePtrWithInfoCallback;
using UniquePtrWithInfoROSMessageCallback =
typename CallbackTypes::UniquePtrWithInfoROSMessageCallback;
using UniquePtrSerializedMessageCallback =
typename CallbackTypes::UniquePtrSerializedMessageCallback;
using UniquePtrSerializedMessageWithInfoCallback =
typename CallbackTypes::UniquePtrSerializedMessageWithInfoCallback;
using SharedConstPtrCallback =
typename CallbackTypes::SharedConstPtrCallback;
using SharedConstPtrROSMessageCallback =
typename CallbackTypes::SharedConstPtrROSMessageCallback;
using SharedConstPtrWithInfoCallback =
typename CallbackTypes::SharedConstPtrWithInfoCallback;
using SharedConstPtrWithInfoROSMessageCallback =
typename CallbackTypes::SharedConstPtrWithInfoROSMessageCallback;
using SharedConstPtrSerializedMessageCallback =
typename CallbackTypes::SharedConstPtrSerializedMessageCallback;
using SharedConstPtrSerializedMessageWithInfoCallback =
typename CallbackTypes::SharedConstPtrSerializedMessageWithInfoCallback;
using ConstRefSharedConstPtrCallback =
typename HelperT::ConstRefSharedConstPtrCallback;
typename CallbackTypes::ConstRefSharedConstPtrCallback;
using ConstRefSharedConstPtrROSMessageCallback =
typename CallbackTypes::ConstRefSharedConstPtrROSMessageCallback;
using ConstRefSharedConstPtrWithInfoCallback =
typename HelperT::ConstRefSharedConstPtrWithInfoCallback;
typename CallbackTypes::ConstRefSharedConstPtrWithInfoCallback;
using ConstRefSharedConstPtrWithInfoROSMessageCallback =
typename CallbackTypes::ConstRefSharedConstPtrWithInfoROSMessageCallback;
using ConstRefSharedConstPtrSerializedMessageCallback =
typename CallbackTypes::ConstRefSharedConstPtrSerializedMessageCallback;
using ConstRefSharedConstPtrSerializedMessageWithInfoCallback =
typename CallbackTypes::ConstRefSharedConstPtrSerializedMessageWithInfoCallback;
using SharedPtrCallback =
typename CallbackTypes::SharedPtrCallback;
using SharedPtrROSMessageCallback =
typename CallbackTypes::SharedPtrROSMessageCallback;
using SharedPtrWithInfoCallback =
typename CallbackTypes::SharedPtrWithInfoCallback;
using SharedPtrWithInfoROSMessageCallback =
typename CallbackTypes::SharedPtrWithInfoROSMessageCallback;
using SharedPtrSerializedMessageCallback =
typename CallbackTypes::SharedPtrSerializedMessageCallback;
using SharedPtrSerializedMessageWithInfoCallback =
typename CallbackTypes::SharedPtrSerializedMessageWithInfoCallback;
using SharedPtrCallback = typename HelperT::SharedPtrCallback;
using SharedPtrWithInfoCallback = typename HelperT::SharedPtrWithInfoCallback;
template<typename T>
struct NotNull
{
NotNull(const T * pointer_in, const char * msg)
: pointer(pointer_in)
{
if (pointer == nullptr) {
throw std::invalid_argument(msg);
}
}
const T * pointer;
};
public:
explicit
AnySubscriptionCallback(const AllocatorT & allocator = AllocatorT()) // NOLINT[runtime/explicit]
: subscribed_type_allocator_(allocator),
ros_message_type_allocator_(allocator)
{
message_allocator_ = allocator;
allocator::set_allocator_for_deleter(&message_deleter_, &message_allocator_);
allocator::set_allocator_for_deleter(&subscribed_type_deleter_, &subscribed_type_allocator_);
allocator::set_allocator_for_deleter(&ros_message_type_deleter_, &ros_message_type_allocator_);
}
[[deprecated("use AnySubscriptionCallback(const AllocatorT & allocator) instead")]]
explicit
AnySubscriptionCallback(std::shared_ptr<AllocatorT> allocator) // NOLINT[runtime/explicit]
{
if (allocator == nullptr) {
throw std::runtime_error("invalid allocator");
}
message_allocator_ = *allocator;
allocator::set_allocator_for_deleter(&message_deleter_, &message_allocator_);
}
: AnySubscriptionCallback(*NotNull<AllocatorT>(allocator.get(), "invalid allocator").pointer)
{}
AnySubscriptionCallback(const AnySubscriptionCallback &) = default;
@@ -190,40 +405,79 @@ public:
}
/// Function for shared_ptr to non-const MessageT, which is deprecated.
template<typename SetT>
// TODO(wjwwood): enable this deprecation after Galactic
// [[deprecated(
// "use 'void (std::shared_ptr<const MessageT>)' instead"
// )]]
void
set_deprecated(std::function<void(std::shared_ptr<MessageT>)> callback)
// set(CallbackT callback)
set_deprecated(std::function<void(std::shared_ptr<SetT>)> callback)
{
callback_variant_ = callback;
}
/// Function for shared_ptr to non-const MessageT with MessageInfo, which is deprecated.
template<typename SetT>
// TODO(wjwwood): enable this deprecation after Galactic
// [[deprecated(
// "use 'void (std::shared_ptr<const MessageT>, const rclcpp::MessageInfo &)' instead"
// )]]
void
set_deprecated(
std::function<void(std::shared_ptr<MessageT>, const rclcpp::MessageInfo &)> callback)
set_deprecated(std::function<void(std::shared_ptr<SetT>, const rclcpp::MessageInfo &)> callback)
{
callback_variant_ = callback;
}
std::unique_ptr<MessageT, MessageDeleter>
create_unique_ptr_from_shared_ptr_message(const std::shared_ptr<const MessageT> & message)
std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter>
create_ros_unique_ptr_from_ros_shared_ptr_message(
const std::shared_ptr<const ROSMessageType> & message)
{
auto ptr = MessageAllocTraits::allocate(message_allocator_, 1);
MessageAllocTraits::construct(message_allocator_, ptr, *message);
return std::unique_ptr<MessageT, MessageDeleter>(ptr, message_deleter_);
auto ptr = ROSMessageTypeAllocatorTraits::allocate(ros_message_type_allocator_, 1);
ROSMessageTypeAllocatorTraits::construct(ros_message_type_allocator_, ptr, *message);
return std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter>(ptr, ros_message_type_deleter_);
}
std::unique_ptr<rclcpp::SerializedMessage, SerializedMessageDeleter>
create_serialized_message_unique_ptr_from_shared_ptr(
const std::shared_ptr<const rclcpp::SerializedMessage> & serialized_message)
{
auto ptr = SerializedMessageAllocatorTraits::allocate(serialized_message_allocator_, 1);
SerializedMessageAllocatorTraits::construct(
serialized_message_allocator_, ptr, *serialized_message);
return std::unique_ptr<
rclcpp::SerializedMessage,
SerializedMessageDeleter
>(ptr, serialized_message_deleter_);
}
std::unique_ptr<SubscribedType, SubscribedTypeDeleter>
create_custom_unique_ptr_from_custom_shared_ptr_message(
const std::shared_ptr<const SubscribedType> & message)
{
auto ptr = SubscribedTypeAllocatorTraits::allocate(subscribed_type_allocator_, 1);
SubscribedTypeAllocatorTraits::construct(subscribed_type_allocator_, ptr, *message);
return std::unique_ptr<SubscribedType, SubscribedTypeDeleter>(ptr, subscribed_type_deleter_);
}
std::unique_ptr<SubscribedType, SubscribedTypeDeleter>
convert_ros_message_to_custom_type_unique_ptr(const ROSMessageType & msg)
{
if constexpr (rclcpp::TypeAdapter<MessageT>::is_specialized::value) {
auto ptr = SubscribedTypeAllocatorTraits::allocate(subscribed_type_allocator_, 1);
SubscribedTypeAllocatorTraits::construct(subscribed_type_allocator_, ptr);
rclcpp::TypeAdapter<MessageT>::convert_to_custom(msg, *ptr);
return std::unique_ptr<SubscribedType, SubscribedTypeDeleter>(ptr, subscribed_type_deleter_);
} else {
throw std::runtime_error(
"convert_ros_message_to_custom_type_unique_ptr "
"unexpectedly called without TypeAdapter");
}
}
// Dispatch when input is a ros message and the output could be anything.
void
dispatch(
std::shared_ptr<MessageT> message,
std::shared_ptr<ROSMessageType> message,
const rclcpp::MessageInfo & message_info)
{
TRACEPOINT(callback_start, static_cast<const void *>(this), false);
@@ -238,28 +492,162 @@ public:
std::visit(
[&message, &message_info, this](auto && callback) {
using T = std::decay_t<decltype(callback)>;
static constexpr bool is_ta = rclcpp::TypeAdapter<MessageT>::is_specialized::value;
if constexpr (std::is_same_v<T, ConstRefCallback>) {
callback(*message);
} else if constexpr (std::is_same_v<T, ConstRefWithInfoCallback>) {
callback(*message, message_info);
} else if constexpr (std::is_same_v<T, UniquePtrCallback>) {
callback(create_unique_ptr_from_shared_ptr_message(message));
} else if constexpr (std::is_same_v<T, UniquePtrWithInfoCallback>) {
callback(create_unique_ptr_from_shared_ptr_message(message), message_info);
// conditions for output is custom message
if constexpr (is_ta && std::is_same_v<T, ConstRefCallback>) {
// TODO(wjwwood): consider avoiding heap allocation for small messages
// maybe something like:
// if constexpr (rosidl_generator_traits::has_fixed_size<T> && sizeof(T) < N) {
// ... on stack
// }
auto local_message = convert_ros_message_to_custom_type_unique_ptr(*message);
callback(*local_message);
} else if constexpr (is_ta && std::is_same_v<T, ConstRefWithInfoCallback>) { // NOLINT
auto local_message = convert_ros_message_to_custom_type_unique_ptr(*message);
callback(*local_message, message_info);
} else if constexpr (is_ta && std::is_same_v<T, UniquePtrCallback>) {
callback(convert_ros_message_to_custom_type_unique_ptr(*message));
} else if constexpr (is_ta && std::is_same_v<T, UniquePtrWithInfoCallback>) {
callback(convert_ros_message_to_custom_type_unique_ptr(*message), message_info);
} else if constexpr ( // NOLINT[readability/braces]
std::is_same_v<T, SharedConstPtrCallback>||
std::is_same_v<T, ConstRefSharedConstPtrCallback>||
std::is_same_v<T, SharedPtrCallback>)
is_ta && (
std::is_same_v<T, SharedConstPtrCallback>||
std::is_same_v<T, ConstRefSharedConstPtrCallback>||
std::is_same_v<T, SharedPtrCallback>
))
{
callback(convert_ros_message_to_custom_type_unique_ptr(*message));
} else if constexpr ( // NOLINT[readability/braces]
is_ta && (
std::is_same_v<T, SharedConstPtrWithInfoCallback>||
std::is_same_v<T, ConstRefSharedConstPtrWithInfoCallback>||
std::is_same_v<T, SharedPtrWithInfoCallback>
))
{
callback(convert_ros_message_to_custom_type_unique_ptr(*message), message_info);
}
// conditions for output is ros message
else if constexpr (std::is_same_v<T, ConstRefROSMessageCallback>) { // NOLINT
callback(*message);
} else if constexpr (std::is_same_v<T, ConstRefWithInfoROSMessageCallback>) {
callback(*message, message_info);
} else if constexpr (std::is_same_v<T, UniquePtrROSMessageCallback>) {
callback(create_ros_unique_ptr_from_ros_shared_ptr_message(message));
} else if constexpr (std::is_same_v<T, UniquePtrWithInfoROSMessageCallback>) {
callback(create_ros_unique_ptr_from_ros_shared_ptr_message(message), message_info);
} else if constexpr ( // NOLINT[readability/braces]
std::is_same_v<T, SharedConstPtrROSMessageCallback>||
std::is_same_v<T, ConstRefSharedConstPtrROSMessageCallback>||
std::is_same_v<T, SharedPtrROSMessageCallback>)
{
callback(message);
} else if constexpr ( // NOLINT[readability/braces]
std::is_same_v<T, SharedConstPtrWithInfoCallback>||
std::is_same_v<T, ConstRefSharedConstPtrWithInfoCallback>||
std::is_same_v<T, SharedPtrWithInfoCallback>)
std::is_same_v<T, SharedConstPtrWithInfoROSMessageCallback>||
std::is_same_v<T, ConstRefSharedConstPtrWithInfoROSMessageCallback>||
std::is_same_v<T, SharedPtrWithInfoROSMessageCallback>)
{
callback(message, message_info);
} else {
}
// condition to catch SerializedMessage types
else if constexpr ( // NOLINT[readability/braces]
std::is_same_v<T, ConstRefSerializedMessageCallback>||
std::is_same_v<T, ConstRefSerializedMessageWithInfoCallback>||
std::is_same_v<T, UniquePtrSerializedMessageCallback>||
std::is_same_v<T, UniquePtrSerializedMessageWithInfoCallback>||
std::is_same_v<T, SharedConstPtrSerializedMessageCallback>||
std::is_same_v<T, SharedConstPtrSerializedMessageWithInfoCallback>||
std::is_same_v<T, ConstRefSharedConstPtrSerializedMessageCallback>||
std::is_same_v<T, ConstRefSharedConstPtrSerializedMessageWithInfoCallback>||
std::is_same_v<T, SharedPtrSerializedMessageCallback>||
std::is_same_v<T, SharedPtrSerializedMessageWithInfoCallback>)
{
throw std::runtime_error(
"Cannot dispatch std::shared_ptr<ROSMessageType> message "
"to rclcpp::SerializedMessage");
}
// condition to catch unhandled callback types
else { // NOLINT[readability/braces]
static_assert(always_false_v<T>, "unhandled callback type");
}
}, callback_variant_);
TRACEPOINT(callback_end, static_cast<const void *>(this));
}
// Dispatch when input is a serialized message and the output could be anything.
void
dispatch(
std::shared_ptr<rclcpp::SerializedMessage> serialized_message,
const rclcpp::MessageInfo & message_info)
{
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) {
// This can happen if it is default initialized, or if it is assigned nullptr.
throw std::runtime_error("dispatch called on an unset AnySubscriptionCallback");
}
}
// Dispatch.
std::visit(
[&serialized_message, &message_info, this](auto && callback) {
using T = std::decay_t<decltype(callback)>;
// condition to catch SerializedMessage types
if constexpr (std::is_same_v<T, ConstRefSerializedMessageCallback>) {
callback(*serialized_message);
} else if constexpr (std::is_same_v<T, ConstRefSerializedMessageWithInfoCallback>) {
callback(*serialized_message, message_info);
} else if constexpr (std::is_same_v<T, UniquePtrSerializedMessageCallback>) {
callback(create_serialized_message_unique_ptr_from_shared_ptr(serialized_message));
} else if constexpr (std::is_same_v<T, UniquePtrSerializedMessageWithInfoCallback>) {
callback(
create_serialized_message_unique_ptr_from_shared_ptr(serialized_message),
message_info);
} else if constexpr ( // NOLINT[readability/braces]
std::is_same_v<T, SharedConstPtrSerializedMessageCallback>||
std::is_same_v<T, ConstRefSharedConstPtrSerializedMessageCallback>||
std::is_same_v<T, SharedPtrSerializedMessageCallback>)
{
callback(create_serialized_message_unique_ptr_from_shared_ptr(serialized_message));
} else if constexpr ( // NOLINT[readability/braces]
std::is_same_v<T, SharedConstPtrSerializedMessageWithInfoCallback>||
std::is_same_v<T, ConstRefSharedConstPtrSerializedMessageWithInfoCallback>||
std::is_same_v<T, SharedPtrSerializedMessageWithInfoCallback>)
{
callback(
create_serialized_message_unique_ptr_from_shared_ptr(serialized_message),
message_info);
}
// conditions for output anything else
else if constexpr ( // NOLINT[whitespace/newline]
std::is_same_v<T, ConstRefCallback>||
std::is_same_v<T, ConstRefROSMessageCallback>||
std::is_same_v<T, ConstRefWithInfoCallback>||
std::is_same_v<T, ConstRefWithInfoROSMessageCallback>||
std::is_same_v<T, UniquePtrCallback>||
std::is_same_v<T, UniquePtrROSMessageCallback>||
std::is_same_v<T, UniquePtrWithInfoCallback>||
std::is_same_v<T, UniquePtrWithInfoROSMessageCallback>||
std::is_same_v<T, SharedConstPtrCallback>||
std::is_same_v<T, SharedConstPtrROSMessageCallback>||
std::is_same_v<T, SharedConstPtrWithInfoCallback>||
std::is_same_v<T, SharedConstPtrWithInfoROSMessageCallback>||
std::is_same_v<T, ConstRefSharedConstPtrCallback>||
std::is_same_v<T, ConstRefSharedConstPtrROSMessageCallback>||
std::is_same_v<T, ConstRefSharedConstPtrWithInfoCallback>||
std::is_same_v<T, ConstRefSharedConstPtrWithInfoROSMessageCallback>||
std::is_same_v<T, SharedPtrCallback>||
std::is_same_v<T, SharedPtrROSMessageCallback>||
std::is_same_v<T, SharedPtrWithInfoCallback>||
std::is_same_v<T, SharedPtrWithInfoROSMessageCallback>)
{
throw std::runtime_error(
"cannot dispatch rclcpp::SerializedMessage to "
"non-rclcpp::SerializedMessage callbacks");
}
// condition to catch unhandled callback types
else { // NOLINT[readability/braces]
static_assert(always_false_v<T>, "unhandled callback type");
}
}, callback_variant_);
@@ -268,7 +656,7 @@ public:
void
dispatch_intra_process(
std::shared_ptr<const MessageT> message,
std::shared_ptr<const ROSMessageType> message,
const rclcpp::MessageInfo & message_info)
{
TRACEPOINT(callback_start, static_cast<const void *>(this), true);
@@ -283,32 +671,89 @@ public:
std::visit(
[&message, &message_info, this](auto && callback) {
using T = std::decay_t<decltype(callback)>;
static constexpr bool is_ta = rclcpp::TypeAdapter<MessageT>::is_specialized::value;
if constexpr (std::is_same_v<T, ConstRefCallback>) {
// conditions for custom type
if constexpr (is_ta && std::is_same_v<T, ConstRefCallback>) {
auto local_message = convert_ros_message_to_custom_type_unique_ptr(*message);
callback(*local_message);
} else if constexpr (is_ta && std::is_same_v<T, ConstRefWithInfoCallback>) { // NOLINT
auto local_message = convert_ros_message_to_custom_type_unique_ptr(*message);
callback(*local_message, message_info);
} else if constexpr ( // NOLINT[readability/braces]
is_ta && (
std::is_same_v<T, UniquePtrCallback>||
std::is_same_v<T, SharedPtrCallback>
))
{
callback(convert_ros_message_to_custom_type_unique_ptr(*message));
} else if constexpr ( // NOLINT[readability/braces]
is_ta && (
std::is_same_v<T, UniquePtrWithInfoCallback>||
std::is_same_v<T, SharedPtrWithInfoCallback>
))
{
callback(convert_ros_message_to_custom_type_unique_ptr(*message), message_info);
} else if constexpr ( // NOLINT[readability/braces]
is_ta && (
std::is_same_v<T, SharedConstPtrCallback>||
std::is_same_v<T, ConstRefSharedConstPtrCallback>
))
{
callback(convert_ros_message_to_custom_type_unique_ptr(*message));
} else if constexpr ( // NOLINT[readability/braces]
is_ta && (
std::is_same_v<T, SharedConstPtrWithInfoCallback>||
std::is_same_v<T, ConstRefSharedConstPtrWithInfoCallback>
))
{
callback(convert_ros_message_to_custom_type_unique_ptr(*message), message_info);
}
// conditions for ros message type
else if constexpr (std::is_same_v<T, ConstRefROSMessageCallback>) { // NOLINT
callback(*message);
} else if constexpr (std::is_same_v<T, ConstRefWithInfoCallback>) {
} else if constexpr (std::is_same_v<T, ConstRefWithInfoROSMessageCallback>) {
callback(*message, message_info);
} else if constexpr ( // NOLINT[readability/braces]
std::is_same_v<T, UniquePtrCallback>||
std::is_same_v<T, SharedPtrCallback>)
std::is_same_v<T, UniquePtrROSMessageCallback>||
std::is_same_v<T, SharedPtrROSMessageCallback>)
{
callback(create_unique_ptr_from_shared_ptr_message(message));
callback(create_ros_unique_ptr_from_ros_shared_ptr_message(message));
} else if constexpr ( // NOLINT[readability/braces]
std::is_same_v<T, UniquePtrWithInfoCallback>||
std::is_same_v<T, SharedPtrWithInfoCallback>)
std::is_same_v<T, UniquePtrWithInfoROSMessageCallback>||
std::is_same_v<T, SharedPtrWithInfoROSMessageCallback>)
{
callback(create_unique_ptr_from_shared_ptr_message(message), message_info);
callback(create_ros_unique_ptr_from_ros_shared_ptr_message(message), message_info);
} else if constexpr ( // NOLINT[readability/braces]
std::is_same_v<T, SharedConstPtrCallback>||
std::is_same_v<T, ConstRefSharedConstPtrCallback>)
std::is_same_v<T, SharedConstPtrROSMessageCallback>||
std::is_same_v<T, ConstRefSharedConstPtrROSMessageCallback>)
{
callback(message);
} else if constexpr ( // NOLINT[readability/braces]
std::is_same_v<T, SharedConstPtrWithInfoCallback>||
std::is_same_v<T, ConstRefSharedConstPtrWithInfoCallback>)
std::is_same_v<T, SharedConstPtrWithInfoROSMessageCallback>||
std::is_same_v<T, ConstRefSharedConstPtrWithInfoROSMessageCallback>)
{
callback(message, message_info);
} else {
}
// condition to catch SerializedMessage types
else if constexpr ( // NOLINT[readability/braces]
std::is_same_v<T, ConstRefSerializedMessageCallback>||
std::is_same_v<T, ConstRefSerializedMessageWithInfoCallback>||
std::is_same_v<T, UniquePtrSerializedMessageCallback>||
std::is_same_v<T, UniquePtrSerializedMessageWithInfoCallback>||
std::is_same_v<T, SharedConstPtrSerializedMessageCallback>||
std::is_same_v<T, SharedConstPtrSerializedMessageWithInfoCallback>||
std::is_same_v<T, ConstRefSharedConstPtrSerializedMessageCallback>||
std::is_same_v<T, ConstRefSharedConstPtrSerializedMessageWithInfoCallback>||
std::is_same_v<T, SharedPtrSerializedMessageCallback>||
std::is_same_v<T, SharedPtrSerializedMessageWithInfoCallback>)
{
throw std::runtime_error(
"Cannot dispatch std::shared_ptr<const ROSMessageType> message "
"to rclcpp::SerializedMessage");
}
// condition to catch unhandled callback types
else { // NOLINT[readability/braces]
static_assert(always_false_v<T>, "unhandled callback type");
}
}, callback_variant_);
@@ -317,7 +762,7 @@ public:
void
dispatch_intra_process(
std::unique_ptr<MessageT, MessageDeleter> message,
std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter> message,
const rclcpp::MessageInfo & message_info)
{
TRACEPOINT(callback_start, static_cast<const void *>(this), true);
@@ -330,28 +775,91 @@ public:
}
// Dispatch.
std::visit(
[&message, &message_info](auto && callback) {
[&message, &message_info, this](auto && callback) {
using T = std::decay_t<decltype(callback)>;
static constexpr bool is_ta = rclcpp::TypeAdapter<MessageT>::is_specialized::value;
if constexpr (std::is_same_v<T, ConstRefCallback>) {
// conditions for custom type
if constexpr (is_ta && std::is_same_v<T, ConstRefCallback>) {
auto local_message = convert_ros_message_to_custom_type_unique_ptr(*message);
callback(*local_message);
} else if constexpr (is_ta && std::is_same_v<T, ConstRefWithInfoCallback>) { // NOLINT
auto local_message = convert_ros_message_to_custom_type_unique_ptr(*message);
callback(*local_message, message_info);
} else if constexpr ( // NOLINT[readability/braces]
is_ta && (
std::is_same_v<T, UniquePtrCallback>||
std::is_same_v<T, SharedPtrCallback>
))
{
callback(convert_ros_message_to_custom_type_unique_ptr(*message));
} else if constexpr ( // NOLINT[readability/braces]
is_ta && (
std::is_same_v<T, UniquePtrWithInfoCallback>||
std::is_same_v<T, SharedPtrWithInfoCallback>
))
{
callback(convert_ros_message_to_custom_type_unique_ptr(*message), message_info);
} else if constexpr ( // NOLINT[readability/braces]
is_ta && (
std::is_same_v<T, SharedConstPtrCallback>||
std::is_same_v<T, ConstRefSharedConstPtrCallback>
))
{
callback(convert_ros_message_to_custom_type_unique_ptr(*message));
} else if constexpr ( // NOLINT[readability/braces]
is_ta && (
std::is_same_v<T, SharedConstPtrWithInfoCallback>||
std::is_same_v<T, ConstRefSharedConstPtrWithInfoCallback>
))
{
callback(convert_ros_message_to_custom_type_unique_ptr(*message), message_info);
}
// conditions for ros message type
else if constexpr (std::is_same_v<T, ConstRefROSMessageCallback>) { // NOLINT
callback(*message);
} else if constexpr (std::is_same_v<T, ConstRefWithInfoCallback>) {
} else if constexpr (std::is_same_v<T, ConstRefWithInfoROSMessageCallback>) {
callback(*message, message_info);
} else if constexpr ( // NOLINT[readability/braces]
std::is_same_v<T, UniquePtrCallback>||
std::is_same_v<T, SharedConstPtrCallback>||
std::is_same_v<T, ConstRefSharedConstPtrCallback>||
std::is_same_v<T, SharedPtrCallback>)
std::is_same_v<T, UniquePtrROSMessageCallback>||
std::is_same_v<T, SharedPtrROSMessageCallback>)
{
callback(std::move(message));
} else if constexpr ( // NOLINT[readability/braces]
std::is_same_v<T, UniquePtrWithInfoCallback>||
std::is_same_v<T, SharedConstPtrWithInfoCallback>||
std::is_same_v<T, ConstRefSharedConstPtrWithInfoCallback>||
std::is_same_v<T, SharedPtrWithInfoCallback>)
std::is_same_v<T, UniquePtrWithInfoROSMessageCallback>||
std::is_same_v<T, SharedPtrWithInfoROSMessageCallback>)
{
callback(std::move(message), message_info);
} else {
} else if constexpr ( // NOLINT[readability/braces]
std::is_same_v<T, SharedConstPtrROSMessageCallback>||
std::is_same_v<T, ConstRefSharedConstPtrROSMessageCallback>)
{
callback(std::move(message));
} else if constexpr ( // NOLINT[readability/braces]
std::is_same_v<T, SharedConstPtrWithInfoROSMessageCallback>||
std::is_same_v<T, ConstRefSharedConstPtrWithInfoROSMessageCallback>)
{
callback(std::move(message), message_info);
}
// condition to catch SerializedMessage types
else if constexpr ( // NOLINT[readability/braces]
std::is_same_v<T, ConstRefSerializedMessageCallback>||
std::is_same_v<T, ConstRefSerializedMessageWithInfoCallback>||
std::is_same_v<T, UniquePtrSerializedMessageCallback>||
std::is_same_v<T, UniquePtrSerializedMessageWithInfoCallback>||
std::is_same_v<T, SharedConstPtrSerializedMessageCallback>||
std::is_same_v<T, SharedConstPtrSerializedMessageWithInfoCallback>||
std::is_same_v<T, ConstRefSharedConstPtrSerializedMessageCallback>||
std::is_same_v<T, ConstRefSharedConstPtrSerializedMessageWithInfoCallback>||
std::is_same_v<T, SharedPtrSerializedMessageCallback>||
std::is_same_v<T, SharedPtrSerializedMessageWithInfoCallback>)
{
throw std::runtime_error(
"Cannot dispatch std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter> message "
"to rclcpp::SerializedMessage");
}
// condition to catch unhandled callback types
else { // NOLINT[readability/braces]
static_assert(always_false_v<T>, "unhandled callback type");
}
}, callback_variant_);
@@ -369,6 +877,18 @@ public:
std::holds_alternative<ConstRefSharedConstPtrWithInfoCallback>(callback_variant_);
}
constexpr
bool
is_serialized_message_callback() const
{
return
std::holds_alternative<ConstRefSerializedMessageCallback>(callback_variant_) ||
std::holds_alternative<UniquePtrSerializedMessageCallback>(callback_variant_) ||
std::holds_alternative<SharedConstPtrSerializedMessageCallback>(callback_variant_) ||
std::holds_alternative<ConstRefSharedConstPtrSerializedMessageCallback>(callback_variant_) ||
std::holds_alternative<SharedPtrSerializedMessageCallback>(callback_variant_);
}
void
register_callback_for_tracing()
{
@@ -402,8 +922,12 @@ private:
// For now, compose the variant into this class as a private attribute.
typename HelperT::variant_type callback_variant_;
MessageAlloc message_allocator_;
MessageDeleter message_deleter_;
SubscribedTypeAllocator subscribed_type_allocator_;
SubscribedTypeDeleter subscribed_type_deleter_;
ROSMessageTypeAllocator ros_message_type_allocator_;
ROSMessageTypeDeleter ros_message_type_deleter_;
SerializedMessageAllocator serialized_message_allocator_;
SerializedMessageDeleter serialized_message_deleter_;
};
} // namespace rclcpp

View File

@@ -17,12 +17,15 @@
#include <atomic>
#include <future>
#include <map>
#include <unordered_map>
#include <memory>
#include <optional> // NOLINT, cpplint doesn't think this is a cpp std header
#include <sstream>
#include <string>
#include <tuple>
#include <utility>
#include <variant> // NOLINT
#include <vector>
#include "rcl/client.h"
#include "rcl/error_handling.h"
@@ -45,6 +48,68 @@
namespace rclcpp
{
namespace detail
{
template<typename FutureT>
struct FutureAndRequestId
{
FutureT future;
int64_t request_id;
FutureAndRequestId(FutureT impl, int64_t req_id)
: future(std::move(impl)), request_id(req_id)
{}
/// Allow implicit conversions to `std::future` by reference.
operator FutureT &() {return this->future;}
/// Deprecated, use the `future` member variable instead.
/**
* Allow implicit conversions to `std::future` by value.
* \deprecated
*/
[[deprecated("FutureAndRequestId: use .future instead of an implicit conversion")]]
operator FutureT() {return this->future;}
// delegate future like methods in the std::future impl_
/// See std::future::get().
auto get() {return this->future.get();}
/// See std::future::valid().
bool valid() const noexcept {return this->future.valid();}
/// See std::future::wait().
void wait() const {return this->future.wait();}
/// See std::future::wait_for().
template<class Rep, class Period>
std::future_status wait_for(
const std::chrono::duration<Rep, Period> & timeout_duration) const
{
return this->future.wait_for(timeout_duration);
}
/// See std::future::wait_until().
template<class Clock, class Duration>
std::future_status wait_until(
const std::chrono::time_point<Clock, Duration> & timeout_time) const
{
return this->future.wait_until(timeout_time);
}
// Rule of five, we could use the rule of zero here, but better be explicit as some of the
// methods are deleted.
/// Move constructor.
FutureAndRequestId(FutureAndRequestId && other) noexcept = default;
/// Deleted copy constructor, each instance is a unique owner of the future.
FutureAndRequestId(const FutureAndRequestId & other) = delete;
/// Move assignment.
FutureAndRequestId & operator=(FutureAndRequestId && other) noexcept = default;
/// Deleted copy assignment, each instance is a unique owner of the future.
FutureAndRequestId & operator=(const FutureAndRequestId & other) = delete;
/// Destructor.
~FutureAndRequestId() = default;
};
} // namespace detail
namespace node_interfaces
{
class NodeBaseInterface;
@@ -178,6 +243,9 @@ template<typename ServiceT>
class Client : public ClientBase
{
public:
using Request = typename ServiceT::Request;
using Response = typename ServiceT::Response;
using SharedRequest = typename ServiceT::Request::SharedPtr;
using SharedResponse = typename ServiceT::Response::SharedPtr;
@@ -187,6 +255,7 @@ public:
using SharedPromise = std::shared_ptr<Promise>;
using SharedPromiseWithRequest = std::shared_ptr<PromiseWithRequest>;
using Future = std::future<SharedResponse>;
using SharedFuture = std::shared_future<SharedResponse>;
using SharedFutureWithRequest = std::shared_future<std::pair<SharedRequest, SharedResponse>>;
@@ -195,6 +264,64 @@ public:
RCLCPP_SMART_PTR_DEFINITIONS(Client)
/// A convenient Client::Future and request id pair.
/**
* Public members:
* - future: a std::future<SharedResponse>.
* - request_id: the request id associated with the future.
*
* All the other methods are equivalent to the ones std::future provides.
*/
struct FutureAndRequestId
: detail::FutureAndRequestId<std::future<SharedResponse>>
{
using detail::FutureAndRequestId<std::future<SharedResponse>>::FutureAndRequestId;
/// Deprecated, use `.future.share()` instead.
/**
* Allow implicit conversions to `std::shared_future` by value.
* \deprecated
*/
[[deprecated(
"FutureAndRequestId: use .future.share() instead of an implicit conversion")]]
operator SharedFuture() {return this->future.share();}
// delegate future like methods in the std::future impl_
/// See std::future::share().
SharedFuture share() noexcept {return this->future.share();}
};
/// A convenient Client::SharedFuture and request id pair.
/**
* Public members:
* - future: a std::shared_future<SharedResponse>.
* - request_id: the request id associated with the future.
*
* All the other methods are equivalent to the ones std::shared_future provides.
*/
struct SharedFutureAndRequestId
: detail::FutureAndRequestId<std::shared_future<SharedResponse>>
{
using detail::FutureAndRequestId<std::shared_future<SharedResponse>>::FutureAndRequestId;
};
/// A convenient Client::SharedFutureWithRequest and request id pair.
/**
* Public members:
* - future: a std::shared_future<SharedResponse>.
* - request_id: the request id associated with the future.
*
* All the other methods are equivalent to the ones std::shared_future provides.
*/
struct SharedFutureWithRequestAndRequestId
: detail::FutureAndRequestId<std::shared_future<std::pair<SharedRequest, SharedResponse>>>
{
using detail::FutureAndRequestId<
std::shared_future<std::pair<SharedRequest, SharedResponse>>
>::FutureAndRequestId;
};
/// Default constructor.
/**
* The constructor for a Client is almost never called directly.
@@ -292,34 +419,89 @@ public:
std::shared_ptr<rmw_request_id_t> request_header,
std::shared_ptr<void> response) override
{
std::unique_lock<std::mutex> lock(pending_requests_mutex_);
auto typed_response = std::static_pointer_cast<typename ServiceT::Response>(response);
int64_t sequence_number = request_header->sequence_number;
// TODO(esteve) this should throw instead since it is not expected to happen in the first place
if (this->pending_requests_.count(sequence_number) == 0) {
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"Received invalid sequence number. Ignoring...");
std::optional<CallbackInfoVariant>
optional_pending_request = this->get_and_erase_pending_request(request_header->sequence_number);
if (!optional_pending_request) {
return;
}
auto tuple = this->pending_requests_[sequence_number];
auto call_promise = std::get<0>(tuple);
auto callback = std::get<1>(tuple);
auto future = std::get<2>(tuple);
this->pending_requests_.erase(sequence_number);
// Unlock here to allow the service to be called recursively from one of its callbacks.
lock.unlock();
call_promise->set_value(typed_response);
callback(future);
auto & value = *optional_pending_request;
auto typed_response = std::static_pointer_cast<typename ServiceT::Response>(
std::move(response));
if (std::holds_alternative<Promise>(value)) {
auto & promise = std::get<Promise>(value);
promise.set_value(std::move(typed_response));
} else if (std::holds_alternative<CallbackTypeValueVariant>(value)) {
auto & inner = std::get<CallbackTypeValueVariant>(value);
const auto & callback = std::get<CallbackType>(inner);
auto & promise = std::get<Promise>(inner);
auto & future = std::get<SharedFuture>(inner);
promise.set_value(std::move(typed_response));
callback(std::move(future));
} else if (std::holds_alternative<CallbackWithRequestTypeValueVariant>(value)) {
auto & inner = std::get<CallbackWithRequestTypeValueVariant>(value);
const auto & callback = std::get<CallbackWithRequestType>(inner);
auto & promise = std::get<PromiseWithRequest>(inner);
auto & future = std::get<SharedFutureWithRequest>(inner);
auto & request = std::get<SharedRequest>(inner);
promise.set_value(std::make_pair(std::move(request), std::move(typed_response)));
callback(std::move(future));
}
}
SharedFuture
/// Send a request to the service server.
/**
* This method returns a `FutureAndRequestId` instance
* that can be passed to Executor::spin_until_future_complete() to
* wait until it has been completed.
*
* If the future never completes,
* e.g. the call to Executor::spin_until_future_complete() times out,
* Client::remove_pending_request() must be called to clean the client internal state.
* Not doing so will make the `Client` instance to use more memory each time a response is not
* received from the service server.
*
* ```cpp
* auto future = client->async_send_request(my_request);
* if (
* rclcpp::FutureReturnCode::TIMEOUT ==
* executor->spin_until_future_complete(future, timeout))
* {
* client->remove_pending_request(future);
* // handle timeout
* } else {
* handle_response(future.get());
* }
* ```
*
* \param[in] request request to be send.
* \return a FutureAndRequestId instance.
*/
FutureAndRequestId
async_send_request(SharedRequest request)
{
return async_send_request(request, [](SharedFuture) {});
Promise promise;
auto future = promise.get_future();
auto req_id = async_send_request_impl(
*request,
std::move(promise));
return FutureAndRequestId(std::move(future), req_id);
}
/// Send a request to the service server and schedule a callback in the executor.
/**
* Similar to the previous overload, but a callback will automatically be called when a response is received.
*
* If the callback is never called, because we never got a reply for the service server, remove_pending_request()
* has to be called with the returned request id or prune_pending_requests().
* Not doing so will make the `Client` instance use more memory each time a response is not
* received from the service server.
* In this case, it's convenient to setup a timer to cleanup the pending requests.
* See for example the `examples_rclcpp_async_client` package in https://github.com/ros2/examples.
*
* \param[in] request request to be send.
* \param[in] cb callback that will be called when we get a response for this request.
* \return the request id representing the request just sent.
*/
template<
typename CallbackT,
typename std::enable_if<
@@ -329,23 +511,28 @@ public:
>::value
>::type * = nullptr
>
SharedFuture
SharedFutureAndRequestId
async_send_request(SharedRequest request, CallbackT && cb)
{
std::lock_guard<std::mutex> lock(pending_requests_mutex_);
int64_t sequence_number;
rcl_ret_t ret = rcl_send_request(get_client_handle().get(), request.get(), &sequence_number);
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "failed to send request");
}
SharedPromise call_promise = std::make_shared<Promise>();
SharedFuture f(call_promise->get_future());
pending_requests_[sequence_number] =
std::make_tuple(call_promise, std::forward<CallbackType>(cb), f);
return f;
Promise promise;
auto shared_future = promise.get_future().share();
auto req_id = async_send_request_impl(
*request,
std::make_tuple(
CallbackType{std::forward<CallbackT>(cb)},
shared_future,
std::move(promise)));
return SharedFutureAndRequestId{std::move(shared_future), req_id};
}
/// Send a request to the service server and schedule a callback in the executor.
/**
* Similar to the previous method, but you can get both the request and response in the callback.
*
* \param[in] request request to be send.
* \param[in] cb callback that will be called when we get a response for this request.
* \return the request id representing the request just sent.
*/
template<
typename CallbackT,
typename std::enable_if<
@@ -355,28 +542,165 @@ public:
>::value
>::type * = nullptr
>
SharedFutureWithRequest
SharedFutureWithRequestAndRequestId
async_send_request(SharedRequest request, CallbackT && cb)
{
SharedPromiseWithRequest promise = std::make_shared<PromiseWithRequest>();
SharedFutureWithRequest future_with_request(promise->get_future());
auto wrapping_cb = [future_with_request, promise, request,
cb = std::forward<CallbackWithRequestType>(cb)](SharedFuture future) {
auto response = future.get();
promise->set_value(std::make_pair(request, response));
cb(future_with_request);
};
async_send_request(request, wrapping_cb);
return future_with_request;
PromiseWithRequest promise;
auto shared_future = promise.get_future().share();
auto req_id = async_send_request_impl(
*request,
std::make_tuple(
CallbackWithRequestType{std::forward<CallbackT>(cb)},
request,
shared_future,
std::move(promise)));
return SharedFutureWithRequestAndRequestId{std::move(shared_future), req_id};
}
/// Cleanup a pending request.
/**
* This notifies the client that we have waited long enough for a response from the server
* to come, we have given up and we are not waiting for a response anymore.
*
* Not calling this will make the client start using more memory for each request
* that never got a reply from the server.
*
* \param[in] request_id request id returned by async_send_request().
* \return true when a pending request was removed, false if not (e.g. a response was received).
*/
bool
remove_pending_request(int64_t request_id)
{
std::lock_guard guard(pending_requests_mutex_);
return pending_requests_.erase(request_id) != 0u;
}
/// Cleanup a pending request.
/**
* Convenient overload, same as:
*
* `Client::remove_pending_request(this, future.request_id)`.
*/
bool
remove_pending_request(const FutureAndRequestId & future)
{
return this->remove_pending_request(future.request_id);
}
/// Cleanup a pending request.
/**
* Convenient overload, same as:
*
* `Client::remove_pending_request(this, future.request_id)`.
*/
bool
remove_pending_request(const SharedFutureAndRequestId & future)
{
return this->remove_pending_request(future.request_id);
}
/// Cleanup a pending request.
/**
* Convenient overload, same as:
*
* `Client::remove_pending_request(this, future.request_id)`.
*/
bool
remove_pending_request(const SharedFutureWithRequestAndRequestId & future)
{
return this->remove_pending_request(future.request_id);
}
/// Clean all pending requests.
/**
* \return number of pending requests that were removed.
*/
size_t
prune_pending_requests()
{
std::lock_guard guard(pending_requests_mutex_);
auto ret = pending_requests_.size();
pending_requests_.clear();
return ret;
}
/// Clean all pending requests older than a time_point.
/**
* \param[in] time_point Requests that were sent before this point are going to be removed.
* \param[inout] pruned_requests Removed requests id will be pushed to the vector
* if a pointer is provided.
* \return number of pending requests that were removed.
*/
template<typename AllocatorT = std::allocator<int64_t>>
size_t
prune_requests_older_than(
std::chrono::time_point<std::chrono::system_clock> time_point,
std::vector<int64_t, AllocatorT> * pruned_requests = nullptr)
{
std::lock_guard guard(pending_requests_mutex_);
auto old_size = pending_requests_.size();
for (auto it = pending_requests_.begin(), last = pending_requests_.end(); it != last; ) {
if (it->second.first < time_point) {
pruned_requests->push_back(it->first);
it = pending_requests_.erase(it);
} else {
++it;
}
}
return old_size - pending_requests_.size();
}
protected:
using CallbackTypeValueVariant = std::tuple<CallbackType, SharedFuture, Promise>;
using CallbackWithRequestTypeValueVariant = std::tuple<
CallbackWithRequestType, SharedRequest, SharedFutureWithRequest, PromiseWithRequest>;
using CallbackInfoVariant = std::variant<
std::promise<SharedResponse>,
CallbackTypeValueVariant,
CallbackWithRequestTypeValueVariant>;
int64_t
async_send_request_impl(const Request & request, CallbackInfoVariant value)
{
int64_t sequence_number;
rcl_ret_t ret = rcl_send_request(get_client_handle().get(), &request, &sequence_number);
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "failed to send request");
}
{
std::lock_guard<std::mutex> lock(pending_requests_mutex_);
pending_requests_.try_emplace(
sequence_number,
std::make_pair(std::chrono::system_clock::now(), std::move(value)));
}
return sequence_number;
}
std::optional<CallbackInfoVariant>
get_and_erase_pending_request(int64_t request_number)
{
std::unique_lock<std::mutex> lock(pending_requests_mutex_);
auto it = this->pending_requests_.find(request_number);
if (it == this->pending_requests_.end()) {
RCUTILS_LOG_DEBUG_NAMED(
"rclcpp",
"Received invalid sequence number. Ignoring...");
return std::nullopt;
}
auto value = std::move(it->second.second);
this->pending_requests_.erase(request_number);
return value;
}
private:
RCLCPP_DISABLE_COPY(Client)
std::map<int64_t, std::tuple<SharedPromise, CallbackType, SharedFuture>> pending_requests_;
std::unordered_map<
int64_t,
std::pair<
std::chrono::time_point<std::chrono::system_clock>,
CallbackInfoVariant>>
pending_requests_;
std::mutex pending_requests_mutex_;
};

View File

@@ -23,6 +23,7 @@
#include <typeindex>
#include <typeinfo>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
@@ -47,6 +48,17 @@ public:
/// Forward declare WeakContextsWrapper
class WeakContextsWrapper;
class OnShutdownCallbackHandle
{
friend class Context;
public:
using OnShutdownCallbackType = std::function<void ()>;
private:
std::weak_ptr<OnShutdownCallbackType> callback;
};
/// Context which encapsulates shared state between nodes and other similar entities.
/**
* A context also represents the lifecycle between init and shutdown of rclcpp.
@@ -80,7 +92,7 @@ public:
*
* Note that this function does not setup any signal handlers, so if you want
* it to be shutdown by the signal handler, then you need to either install
* them manually with rclcpp::install_signal_handers() or use rclcpp::init().
* them manually with rclcpp::install_signal_handlers() or use rclcpp::init().
* In addition to installing the signal handlers, the shutdown_on_sigint
* of the InitOptions needs to be `true` for this context to be shutdown by
* the signal handler, otherwise it will be passed over.
@@ -177,7 +189,7 @@ public:
bool
shutdown(const std::string & reason);
using OnShutdownCallback = std::function<void ()>;
using OnShutdownCallback = OnShutdownCallbackHandle::OnShutdownCallbackType;
/// Add a on_shutdown callback to be called when shutdown is called for this context.
/**
@@ -203,23 +215,47 @@ public:
OnShutdownCallback
on_shutdown(OnShutdownCallback callback);
/// Return the shutdown callbacks as const.
/// Add a on_shutdown callback to be called when shutdown is called for this context.
/**
* Using the returned reference is not thread-safe with calls that modify
* the list of "on shutdown" callbacks, i.e. on_shutdown().
* These callbacks will be called in the order they are added as the second
* to last step in shutdown().
*
* When shutdown occurs due to the signal handler, these callbacks are run
* asynchronously in the dedicated singal handling thread.
*
* Also, shutdown() may be called from the destructor of this function.
* Therefore, it is not safe to throw exceptions from these callbacks.
* Instead, log errors or use some other mechanism to indicate an error has
* occurred.
*
* On shutdown callbacks may be registered before init and after shutdown,
* and persist on repeated init's.
*
* \param[in] callback the on_shutdown callback to be registered
* \return the created callback handle
*/
RCLCPP_PUBLIC
const std::vector<OnShutdownCallback> &
get_on_shutdown_callbacks() const;
virtual
OnShutdownCallbackHandle
add_on_shutdown_callback(OnShutdownCallback callback);
/// Remove an registered on_shutdown callbacks.
/**
* \param[in] callback_handle the on_shutdown callback handle to be removed.
* \return true if the callback is found and removed, otherwise false.
*/
RCLCPP_PUBLIC
virtual
bool
remove_on_shutdown_callback(const OnShutdownCallbackHandle & callback_handle);
/// Return the shutdown callbacks.
/**
* Using the returned reference is not thread-safe with calls that modify
* the list of "on shutdown" callbacks, i.e. on_shutdown().
* Returned callbacks are a copy of the registered callbacks.
*/
RCLCPP_PUBLIC
std::vector<OnShutdownCallback> &
get_on_shutdown_callbacks();
std::vector<OnShutdownCallback>
get_on_shutdown_callbacks() const;
/// Return the internal rcl context.
RCLCPP_PUBLIC
@@ -299,8 +335,8 @@ private:
// attempt to acquire another sub context.
std::recursive_mutex sub_contexts_mutex_;
std::vector<OnShutdownCallback> on_shutdown_callbacks_;
std::mutex on_shutdown_callbacks_mutex_;
std::unordered_set<std::shared_ptr<OnShutdownCallback>> on_shutdown_callbacks_;
mutable std::mutex on_shutdown_callbacks_mutex_;
/// Condition variable for timed sleep (see sleep_for).
std::condition_variable interrupt_condition_variable_;

View File

@@ -47,11 +47,11 @@ template<
typename MessageT,
typename CallbackT,
typename AllocatorT,
typename CallbackMessageT,
typename SubscriptionT,
typename MessageMemoryStrategyT,
typename NodeParametersT,
typename NodeTopicsT>
typename NodeTopicsT,
typename ROSMessageType = typename SubscriptionT::ROSMessageType>
typename std::shared_ptr<SubscriptionT>
create_subscription(
NodeParametersT & node_parameters,
@@ -70,7 +70,7 @@ create_subscription(
using rclcpp::node_interfaces::get_node_topics_interface;
auto node_topics_interface = get_node_topics_interface(node_topics);
std::shared_ptr<rclcpp::topic_statistics::SubscriptionTopicStatistics<CallbackMessageT>>
std::shared_ptr<rclcpp::topic_statistics::SubscriptionTopicStatistics<ROSMessageType>>
subscription_topic_stats = nullptr;
if (rclcpp::detail::resolve_enable_topic_statistics(
@@ -92,11 +92,11 @@ create_subscription(
qos);
subscription_topic_stats = std::make_shared<
rclcpp::topic_statistics::SubscriptionTopicStatistics<CallbackMessageT>
rclcpp::topic_statistics::SubscriptionTopicStatistics<ROSMessageType>
>(node_topics_interface->get_node_base_interface()->get_name(), publisher);
std::weak_ptr<
rclcpp::topic_statistics::SubscriptionTopicStatistics<CallbackMessageT>
rclcpp::topic_statistics::SubscriptionTopicStatistics<ROSMessageType>
> weak_subscription_topic_stats(subscription_topic_stats);
auto sub_call_back = [weak_subscription_topic_stats]() {
auto subscription_topic_stats = weak_subscription_topic_stats.lock();
@@ -153,7 +153,6 @@ create_subscription(
* \tparam MessageT
* \tparam CallbackT
* \tparam AllocatorT
* \tparam CallbackMessageT
* \tparam SubscriptionT
* \tparam MessageMemoryStrategyT
* \tparam NodeT
@@ -171,13 +170,8 @@ template<
typename MessageT,
typename CallbackT,
typename AllocatorT = std::allocator<void>,
typename CallbackMessageT =
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type,
typename SubscriptionT = rclcpp::Subscription<CallbackMessageT, AllocatorT>,
typename MessageMemoryStrategyT = rclcpp::message_memory_strategy::MessageMemoryStrategy<
CallbackMessageT,
AllocatorT
>,
typename SubscriptionT = rclcpp::Subscription<MessageT, AllocatorT>,
typename MessageMemoryStrategyT = typename SubscriptionT::MessageMemoryStrategyType,
typename NodeT>
typename std::shared_ptr<SubscriptionT>
create_subscription(
@@ -194,7 +188,7 @@ create_subscription(
)
{
return rclcpp::detail::create_subscription<
MessageT, CallbackT, AllocatorT, CallbackMessageT, SubscriptionT, MessageMemoryStrategyT>(
MessageT, CallbackT, AllocatorT, SubscriptionT, MessageMemoryStrategyT>(
node, node, topic_name, qos, std::forward<CallbackT>(callback), options, msg_mem_strat);
}
@@ -206,13 +200,8 @@ template<
typename MessageT,
typename CallbackT,
typename AllocatorT = std::allocator<void>,
typename CallbackMessageT =
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type,
typename SubscriptionT = rclcpp::Subscription<CallbackMessageT, AllocatorT>,
typename MessageMemoryStrategyT = rclcpp::message_memory_strategy::MessageMemoryStrategy<
CallbackMessageT,
AllocatorT
>>
typename SubscriptionT = rclcpp::Subscription<MessageT, AllocatorT>,
typename MessageMemoryStrategyT = typename SubscriptionT::MessageMemoryStrategyType>
typename std::shared_ptr<SubscriptionT>
create_subscription(
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr & node_parameters,
@@ -229,7 +218,7 @@ create_subscription(
)
{
return rclcpp::detail::create_subscription<
MessageT, CallbackT, AllocatorT, CallbackMessageT, SubscriptionT, MessageMemoryStrategyT>(
MessageT, CallbackT, AllocatorT, SubscriptionT, MessageMemoryStrategyT>(
node_parameters, node_topics, topic_name, qos,
std::forward<CallbackT>(callback), options, msg_mem_strat);
}

View File

@@ -1,76 +0,0 @@
// Copyright 2021 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__DETAIL__MUTEX_TWO_PRIORITIES_HPP_
#define RCLCPP__DETAIL__MUTEX_TWO_PRIORITIES_HPP_
#include <condition_variable>
#include <mutex>
namespace rclcpp
{
namespace detail
{
/// \internal A mutex that has two locking mechanism, one with higher priority than the other.
/**
* After the current mutex owner release the lock, a thread that used the high
* priority mechanism will have priority over threads that used the low priority mechanism.
*/
class MutexTwoPriorities
{
public:
class HighPriorityLockable
{
public:
explicit HighPriorityLockable(MutexTwoPriorities & parent);
void lock();
void unlock();
private:
MutexTwoPriorities & parent_;
};
class LowPriorityLockable
{
public:
explicit LowPriorityLockable(MutexTwoPriorities & parent);
void lock();
void unlock();
private:
MutexTwoPriorities & parent_;
};
HighPriorityLockable
get_high_priority_lockable();
LowPriorityLockable
get_low_priority_lockable();
private:
std::condition_variable hp_cv_;
std::condition_variable lp_cv_;
std::mutex cv_mutex_;
size_t hp_waiting_count_{0u};
bool data_taken_{false};
};
} // namespace detail
} // namespace rclcpp
#endif // RCLCPP__DETAIL__MUTEX_TWO_PRIORITIES_HPP_

View File

@@ -282,8 +282,8 @@ class ParameterModifiedInCallbackException : public std::runtime_error
using std::runtime_error::runtime_error;
};
/// Thrown when a parameter override wasn't provided and one was required.
class NoParameterOverrideProvided : public std::runtime_error
/// Thrown when an uninitialized parameter is accessed.
class ParameterUninitializedException : public std::runtime_error
{
public:
/// Construct an instance.
@@ -291,8 +291,8 @@ public:
* \param[in] name the name of the parameter.
* \param[in] message custom exception message.
*/
explicit NoParameterOverrideProvided(const std::string & name)
: std::runtime_error("parameter '" + name + "' requires an user provided parameter override")
explicit ParameterUninitializedException(const std::string & name)
: std::runtime_error("parameter '" + name + "' is not initialized")
{}
};

View File

@@ -29,7 +29,9 @@
#include "rcl/guard_condition.h"
#include "rcl/wait.h"
#include "rcpputils/scope_exit.hpp"
#include "rclcpp/context.hpp"
#include "rclcpp/contexts/default_context.hpp"
#include "rclcpp/guard_condition.hpp"
#include "rclcpp/executor_options.hpp"
@@ -39,7 +41,6 @@
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/utilities.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rclcpp/scope_exit.hpp"
namespace rclcpp
{
@@ -353,7 +354,7 @@ public:
if (spinning.exchange(true)) {
throw std::runtime_error("spin_until_future_complete() called while already spinning");
}
RCLCPP_SCOPE_EXIT(this->spinning.store(false); );
RCPPUTILS_SCOPE_EXIT(this->spinning.store(false); );
while (rclcpp::ok(this->context_) && spinning.load()) {
// Do one item of work.
spin_once_impl(timeout_left);
@@ -571,6 +572,9 @@ protected:
/// nodes that are associated with the executor
std::list<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>
weak_nodes_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// shutdown callback handle registered to Context
rclcpp::OnShutdownCallbackHandle shutdown_callback_handle_;
};
} // namespace rclcpp

View File

@@ -22,7 +22,6 @@
#include <thread>
#include <unordered_map>
#include "rclcpp/detail/mutex_two_priorities.hpp"
#include "rclcpp/executor.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/memory_strategies.hpp"
@@ -82,12 +81,10 @@ protected:
private:
RCLCPP_DISABLE_COPY(MultiThreadedExecutor)
detail::MutexTwoPriorities wait_mutex_;
std::mutex wait_mutex_;
size_t number_of_threads_;
bool yield_before_execute_;
std::chrono::nanoseconds next_exec_timeout_;
std::set<TimerBase::SharedPtr> scheduled_timers_;
};
} // namespace executors

View File

@@ -57,15 +57,13 @@ public:
/**
* \param p_wait_set A reference to the wait set to be used in the executor
* \param memory_strategy Shared pointer to the memory strategy to set.
* \param executor_guard_condition executor's guard condition
* \throws std::runtime_error if memory strategy is null
*/
RCLCPP_PUBLIC
void
init(
rcl_wait_set_t * p_wait_set,
rclcpp::memory_strategy::MemoryStrategy::SharedPtr memory_strategy,
rcl_guard_condition_t * executor_guard_condition);
rclcpp::memory_strategy::MemoryStrategy::SharedPtr memory_strategy);
/// Finalize StaticExecutorEntitiesCollector to clear resources
RCLCPP_PUBLIC
@@ -338,6 +336,10 @@ private:
/// List of weak nodes registered in the static executor
std::list<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr> weak_nodes_;
// Mutex to protect vector of new nodes.
std::mutex new_nodes_mutex_;
std::vector<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr> new_nodes_;
/// Wait set for managing entities that the rmw layer waits on.
rcl_wait_set_t * p_wait_set_ = nullptr;

View File

@@ -24,6 +24,7 @@
#include "rclcpp/experimental/buffers/intra_process_buffer.hpp"
#include "rclcpp/experimental/buffers/ring_buffer_implementation.hpp"
#include "rclcpp/intra_process_buffer_type.hpp"
#include "rclcpp/qos.hpp"
namespace rclcpp
{
@@ -37,13 +38,13 @@ template<
typename rclcpp::experimental::buffers::IntraProcessBuffer<MessageT, Alloc, Deleter>::UniquePtr
create_intra_process_buffer(
IntraProcessBufferType buffer_type,
rmw_qos_profile_t qos,
const rclcpp::QoS & qos,
std::shared_ptr<Alloc> allocator)
{
using MessageSharedPtr = std::shared_ptr<const MessageT>;
using MessageUniquePtr = std::unique_ptr<MessageT, Deleter>;
size_t buffer_size = qos.depth;
size_t buffer_size = qos.depth();
using rclcpp::experimental::buffers::IntraProcessBuffer;
typename IntraProcessBuffer<MessageT, Alloc, Deleter>::UniquePtr buffer;

View File

@@ -33,6 +33,7 @@
#include "rclcpp/allocator/allocator_deleter.hpp"
#include "rclcpp/experimental/subscription_intra_process.hpp"
#include "rclcpp/experimental/subscription_intra_process_base.hpp"
#include "rclcpp/experimental/subscription_intra_process_buffer.hpp"
#include "rclcpp/logger.hpp"
#include "rclcpp/logging.hpp"
#include "rclcpp/macros.hpp"
@@ -181,7 +182,7 @@ public:
do_intra_process_publish(
uint64_t intra_process_publisher_id,
std::unique_ptr<MessageT, Deleter> message,
std::shared_ptr<typename allocator::AllocRebind<MessageT, Alloc>::allocator_type> allocator)
typename allocator::AllocRebind<MessageT, Alloc>::allocator_type & allocator)
{
using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
using MessageAllocatorT = typename MessageAllocTraits::allocator_type;
@@ -202,7 +203,8 @@ public:
// None of the buffers require ownership, so we promote the pointer
std::shared_ptr<MessageT> msg = std::move(message);
this->template add_shared_msg_to_buffers<MessageT>(msg, sub_ids.take_shared_subscriptions);
this->template add_shared_msg_to_buffers<MessageT, Alloc, Deleter>(
msg, sub_ids.take_shared_subscriptions);
} else if (!sub_ids.take_ownership_subscriptions.empty() && // NOLINT
sub_ids.take_shared_subscriptions.size() <= 1)
{
@@ -225,9 +227,9 @@ public:
{
// Construct a new shared pointer from the message
// for the buffers that do not require ownership
auto shared_msg = std::allocate_shared<MessageT, MessageAllocatorT>(*allocator, *message);
auto shared_msg = std::allocate_shared<MessageT, MessageAllocatorT>(allocator, *message);
this->template add_shared_msg_to_buffers<MessageT>(
this->template add_shared_msg_to_buffers<MessageT, Alloc, Deleter>(
shared_msg, sub_ids.take_shared_subscriptions);
this->template add_owned_msg_to_buffers<MessageT, Alloc, Deleter>(
std::move(message), sub_ids.take_ownership_subscriptions, allocator);
@@ -242,7 +244,7 @@ public:
do_intra_process_publish_and_return_shared(
uint64_t intra_process_publisher_id,
std::unique_ptr<MessageT, Deleter> message,
std::shared_ptr<typename allocator::AllocRebind<MessageT, Alloc>::allocator_type> allocator)
typename allocator::AllocRebind<MessageT, Alloc>::allocator_type & allocator)
{
using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
using MessageAllocatorT = typename MessageAllocTraits::allocator_type;
@@ -263,17 +265,17 @@ public:
// If there are no owning, just convert to shared.
std::shared_ptr<MessageT> shared_msg = std::move(message);
if (!sub_ids.take_shared_subscriptions.empty()) {
this->template add_shared_msg_to_buffers<MessageT>(
this->template add_shared_msg_to_buffers<MessageT, Alloc, Deleter>(
shared_msg, sub_ids.take_shared_subscriptions);
}
return shared_msg;
} else {
// Construct a new shared pointer from the message for the buffers that
// do not require ownership and to return.
auto shared_msg = std::allocate_shared<MessageT, MessageAllocatorT>(*allocator, *message);
auto shared_msg = std::allocate_shared<MessageT, MessageAllocatorT>(allocator, *message);
if (!sub_ids.take_shared_subscriptions.empty()) {
this->template add_shared_msg_to_buffers<MessageT>(
this->template add_shared_msg_to_buffers<MessageT, Alloc, Deleter>(
shared_msg,
sub_ids.take_shared_subscriptions);
}
@@ -303,25 +305,6 @@ public:
get_subscription_intra_process(uint64_t intra_process_subscription_id);
private:
struct SubscriptionInfo
{
SubscriptionInfo() = default;
rclcpp::experimental::SubscriptionIntraProcessBase::WeakPtr subscription;
rmw_qos_profile_t qos;
const char * topic_name;
bool use_take_shared_method;
};
struct PublisherInfo
{
PublisherInfo() = default;
rclcpp::PublisherBase::WeakPtr publisher;
rmw_qos_profile_t qos;
const char * topic_name;
};
struct SplittedSubscriptions
{
std::vector<uint64_t> take_shared_subscriptions;
@@ -329,10 +312,10 @@ private:
};
using SubscriptionMap =
std::unordered_map<uint64_t, SubscriptionInfo>;
std::unordered_map<uint64_t, rclcpp::experimental::SubscriptionIntraProcessBase::WeakPtr>;
using PublisherMap =
std::unordered_map<uint64_t, PublisherInfo>;
std::unordered_map<uint64_t, rclcpp::PublisherBase::WeakPtr>;
using PublisherToSubscriptionIdsMap =
std::unordered_map<uint64_t, SplittedSubscriptions>;
@@ -348,9 +331,14 @@ private:
RCLCPP_PUBLIC
bool
can_communicate(PublisherInfo pub_info, SubscriptionInfo sub_info) const;
can_communicate(
rclcpp::PublisherBase::SharedPtr pub,
rclcpp::experimental::SubscriptionIntraProcessBase::SharedPtr sub) const;
template<typename MessageT>
template<
typename MessageT,
typename Alloc,
typename Deleter>
void
add_shared_msg_to_buffers(
std::shared_ptr<const MessageT> message,
@@ -361,11 +349,18 @@ private:
if (subscription_it == subscriptions_.end()) {
throw std::runtime_error("subscription has unexpectedly gone out of scope");
}
auto subscription_base = subscription_it->second.subscription.lock();
auto subscription_base = subscription_it->second.lock();
if (subscription_base) {
auto subscription = std::static_pointer_cast<
rclcpp::experimental::SubscriptionIntraProcess<MessageT>
auto subscription = std::dynamic_pointer_cast<
rclcpp::experimental::SubscriptionIntraProcessBuffer<MessageT, Alloc, Deleter>
>(subscription_base);
if (nullptr == subscription) {
throw std::runtime_error(
"failed to dynamic cast SubscriptionIntraProcessBase to "
"SubscriptionIntraProcessBuffer<MessageT, Alloc, Deleter>, which "
"can happen when the publisher and subscription use different "
"allocator types, which is not supported");
}
subscription->provide_intra_process_message(message);
} else {
@@ -382,7 +377,7 @@ private:
add_owned_msg_to_buffers(
std::unique_ptr<MessageT, Deleter> message,
std::vector<uint64_t> subscription_ids,
std::shared_ptr<typename allocator::AllocRebind<MessageT, Alloc>::allocator_type> allocator)
typename allocator::AllocRebind<MessageT, Alloc>::allocator_type & allocator)
{
using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
using MessageUniquePtr = std::unique_ptr<MessageT, Deleter>;
@@ -392,11 +387,18 @@ private:
if (subscription_it == subscriptions_.end()) {
throw std::runtime_error("subscription has unexpectedly gone out of scope");
}
auto subscription_base = subscription_it->second.subscription.lock();
auto subscription_base = subscription_it->second.lock();
if (subscription_base) {
auto subscription = std::static_pointer_cast<
rclcpp::experimental::SubscriptionIntraProcess<MessageT>
auto subscription = std::dynamic_pointer_cast<
rclcpp::experimental::SubscriptionIntraProcessBuffer<MessageT, Alloc, Deleter>
>(subscription_base);
if (nullptr == subscription) {
throw std::runtime_error(
"failed to dynamic cast SubscriptionIntraProcessBase to "
"SubscriptionIntraProcessBuffer<MessageT, Alloc, Deleter>, which "
"can happen when the publisher and subscription use different "
"allocator types, which is not supported");
}
if (std::next(it) == subscription_ids.end()) {
// If this is the last subscription, give up ownership
@@ -405,8 +407,8 @@ private:
// Copy the message since we have additional subscriptions to serve
MessageUniquePtr copy_message;
Deleter deleter = message.get_deleter();
auto ptr = MessageAllocTraits::allocate(*allocator.get(), 1);
MessageAllocTraits::construct(*allocator.get(), ptr, *message);
auto ptr = MessageAllocTraits::allocate(allocator, 1);
MessageAllocTraits::construct(allocator, ptr, *message);
copy_message = MessageUniquePtr(ptr, deleter);
subscription->provide_intra_process_message(std::move(copy_message));

View File

@@ -30,6 +30,8 @@
#include "rclcpp/experimental/buffers/intra_process_buffer.hpp"
#include "rclcpp/experimental/create_intra_process_buffer.hpp"
#include "rclcpp/experimental/subscription_intra_process_base.hpp"
#include "rclcpp/experimental/subscription_intra_process_buffer.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/type_support_decl.hpp"
#include "rclcpp/waitable.hpp"
#include "tracetools/tracetools.h"
@@ -44,54 +46,43 @@ template<
typename Alloc = std::allocator<void>,
typename Deleter = std::default_delete<MessageT>,
typename CallbackMessageT = MessageT>
class SubscriptionIntraProcess : public SubscriptionIntraProcessBase
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(SubscriptionIntraProcess)
using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
using MessageAlloc = typename MessageAllocTraits::allocator_type;
using ConstMessageSharedPtr = std::shared_ptr<const MessageT>;
using MessageUniquePtr = std::unique_ptr<MessageT, Deleter>;
using BufferUniquePtr = typename rclcpp::experimental::buffers::IntraProcessBuffer<
class SubscriptionIntraProcess
: public SubscriptionIntraProcessBuffer<
MessageT,
Alloc,
Deleter
>::UniquePtr;
>
{
using SubscriptionIntraProcessBufferT = SubscriptionIntraProcessBuffer<
MessageT,
Alloc,
Deleter
>;
public:
RCLCPP_SMART_PTR_DEFINITIONS(SubscriptionIntraProcess)
using MessageAllocTraits = typename SubscriptionIntraProcessBufferT::MessageAllocTraits;
using MessageAlloc = typename SubscriptionIntraProcessBufferT::MessageAlloc;
using ConstMessageSharedPtr = typename SubscriptionIntraProcessBufferT::ConstMessageSharedPtr;
using MessageUniquePtr = typename SubscriptionIntraProcessBufferT::MessageUniquePtr;
using BufferUniquePtr = typename SubscriptionIntraProcessBufferT::BufferUniquePtr;
SubscriptionIntraProcess(
AnySubscriptionCallback<CallbackMessageT, Alloc> callback,
std::shared_ptr<Alloc> allocator,
rclcpp::Context::SharedPtr context,
const std::string & topic_name,
rmw_qos_profile_t qos_profile,
const rclcpp::QoS & qos_profile,
rclcpp::IntraProcessBufferType buffer_type)
: SubscriptionIntraProcessBase(topic_name, qos_profile),
: SubscriptionIntraProcessBuffer<MessageT, Alloc, Deleter>(
allocator,
context,
topic_name,
qos_profile,
buffer_type),
any_callback_(callback)
{
if (!std::is_same<MessageT, CallbackMessageT>::value) {
throw std::runtime_error("SubscriptionIntraProcess wrong callback type");
}
// Create the intra-process buffer.
buffer_ = rclcpp::experimental::create_intra_process_buffer<MessageT, Alloc, Deleter>(
buffer_type,
qos_profile,
allocator);
// Create the guard condition.
rcl_guard_condition_options_t guard_condition_options =
rcl_guard_condition_get_default_options();
gc_ = rcl_get_zero_initialized_guard_condition();
rcl_ret_t ret = rcl_guard_condition_init(
&gc_, context->get_rcl_context().get(), guard_condition_options);
if (RCL_RET_OK != ret) {
throw std::runtime_error("SubscriptionIntraProcess init error initializing guard condition");
}
TRACEPOINT(
rclcpp_subscription_callback_added,
static_cast<const void *>(this),
@@ -104,22 +95,7 @@ public:
#endif
}
~SubscriptionIntraProcess()
{
if (rcl_guard_condition_fini(&gc_) != RCL_RET_OK) {
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"Failed to destroy guard condition: %s",
rcutils_get_error_string().str);
}
}
bool
is_ready(rcl_wait_set_t * wait_set)
{
(void) wait_set;
return buffer_->has_data();
}
virtual ~SubscriptionIntraProcess() = default;
std::shared_ptr<void>
take_data()
@@ -128,9 +104,9 @@ public:
MessageUniquePtr unique_msg;
if (any_callback_.use_take_shared_method()) {
shared_msg = buffer_->consume_shared();
shared_msg = this->buffer_->consume_shared();
} else {
unique_msg = buffer_->consume_unique();
unique_msg = this->buffer_->consume_unique();
}
return std::static_pointer_cast<void>(
std::make_shared<std::pair<ConstMessageSharedPtr, MessageUniquePtr>>(
@@ -141,37 +117,10 @@ public:
void execute(std::shared_ptr<void> & data)
{
execute_impl<CallbackMessageT>(data);
}
void
provide_intra_process_message(ConstMessageSharedPtr message)
{
buffer_->add_shared(std::move(message));
trigger_guard_condition();
}
void
provide_intra_process_message(MessageUniquePtr message)
{
buffer_->add_unique(std::move(message));
trigger_guard_condition();
}
bool
use_take_shared_method() const
{
return buffer_->use_take_shared_method();
}
private:
void
trigger_guard_condition()
{
rcl_ret_t ret = rcl_trigger_guard_condition(&gc_);
(void)ret;
execute_impl<MessageT>(data);
}
protected:
template<typename T>
typename std::enable_if<std::is_same<T, rcl_serialized_message_t>::value, void>::type
execute_impl(std::shared_ptr<void> & data)
@@ -206,7 +155,6 @@ private:
}
AnySubscriptionCallback<CallbackMessageT, Alloc> any_callback_;
BufferUniquePtr buffer_;
};
} // namespace experimental

View File

@@ -25,6 +25,7 @@
#include "rcl/error_handling.h"
#include "rclcpp/qos.hpp"
#include "rclcpp/type_support_decl.hpp"
#include "rclcpp/waitable.hpp"
@@ -39,7 +40,9 @@ public:
RCLCPP_SMART_PTR_ALIASES_ONLY(SubscriptionIntraProcessBase)
RCLCPP_PUBLIC
SubscriptionIntraProcessBase(const std::string & topic_name, rmw_qos_profile_t qos_profile)
SubscriptionIntraProcessBase(
const std::string & topic_name,
const rclcpp::QoS & qos_profile)
: topic_name_(topic_name), qos_profile_(qos_profile)
{}
@@ -71,7 +74,7 @@ public:
get_topic_name() const;
RCLCPP_PUBLIC
rmw_qos_profile_t
QoS
get_actual_qos() const;
protected:
@@ -83,7 +86,7 @@ private:
trigger_guard_condition() = 0;
std::string topic_name_;
rmw_qos_profile_t qos_profile_;
QoS qos_profile_;
};
} // namespace experimental

View File

@@ -0,0 +1,143 @@
// Copyright 2021 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_BUFFER_HPP_
#define RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_BUFFER_HPP_
#include <rmw/rmw.h>
#include <functional>
#include <map>
#include <memory>
#include <stdexcept>
#include <string>
#include <utility>
#include "rcl/error_handling.h"
#include "rclcpp/any_subscription_callback.hpp"
#include "rclcpp/experimental/buffers/intra_process_buffer.hpp"
#include "rclcpp/experimental/create_intra_process_buffer.hpp"
#include "rclcpp/experimental/subscription_intra_process_base.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/type_support_decl.hpp"
#include "rclcpp/waitable.hpp"
#include "tracetools/tracetools.h"
namespace rclcpp
{
namespace experimental
{
template<
typename MessageT,
typename Alloc = std::allocator<void>,
typename Deleter = std::default_delete<MessageT>
>
class SubscriptionIntraProcessBuffer : public SubscriptionIntraProcessBase
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(SubscriptionIntraProcessBuffer)
using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
using MessageAlloc = typename MessageAllocTraits::allocator_type;
using ConstMessageSharedPtr = std::shared_ptr<const MessageT>;
using MessageUniquePtr = std::unique_ptr<MessageT, Deleter>;
using BufferUniquePtr = typename rclcpp::experimental::buffers::IntraProcessBuffer<
MessageT,
Alloc,
Deleter
>::UniquePtr;
SubscriptionIntraProcessBuffer(
std::shared_ptr<Alloc> allocator,
rclcpp::Context::SharedPtr context,
const std::string & topic_name,
const rclcpp::QoS & qos_profile,
rclcpp::IntraProcessBufferType buffer_type)
: SubscriptionIntraProcessBase(topic_name, qos_profile)
{
// Create the intra-process buffer.
buffer_ = rclcpp::experimental::create_intra_process_buffer<MessageT, Alloc, Deleter>(
buffer_type,
qos_profile,
allocator);
// Create the guard condition.
rcl_guard_condition_options_t guard_condition_options =
rcl_guard_condition_get_default_options();
gc_ = rcl_get_zero_initialized_guard_condition();
rcl_ret_t ret = rcl_guard_condition_init(
&gc_, context->get_rcl_context().get(), guard_condition_options);
if (RCL_RET_OK != ret) {
throw std::runtime_error(
"SubscriptionIntraProcessBuffer init error initializing guard condition");
}
}
virtual ~SubscriptionIntraProcessBuffer()
{
if (rcl_guard_condition_fini(&gc_) != RCL_RET_OK) {
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"Failed to destroy guard condition: %s",
rcutils_get_error_string().str);
}
}
bool
is_ready(rcl_wait_set_t * wait_set)
{
(void) wait_set;
return buffer_->has_data();
}
void
provide_intra_process_message(ConstMessageSharedPtr message)
{
buffer_->add_shared(std::move(message));
trigger_guard_condition();
}
void
provide_intra_process_message(MessageUniquePtr message)
{
buffer_->add_unique(std::move(message));
trigger_guard_condition();
}
bool
use_take_shared_method() const
{
return buffer_->use_take_shared_method();
}
protected:
void
trigger_guard_condition()
{
rcl_ret_t ret = rcl_trigger_guard_condition(&gc_);
(void)ret;
}
BufferUniquePtr buffer_;
};
} // namespace experimental
} // namespace rclcpp
#endif // RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_BUFFER_HPP_

View File

@@ -136,6 +136,13 @@ public:
void handle_message(
std::shared_ptr<void> & message, const rclcpp::MessageInfo & message_info) override;
/// Handle dispatching rclcpp::SerializedMessage to user callback.
RCLCPP_PUBLIC
void
handle_serialized_message(
const std::shared_ptr<rclcpp::SerializedMessage> & serialized_message,
const rclcpp::MessageInfo & message_info) override;
/// This function is currently not implemented.
RCLCPP_PUBLIC
void handle_loaned_message(

View File

@@ -0,0 +1,90 @@
// Copyright 2021 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__GET_MESSAGE_TYPE_SUPPORT_HANDLE_HPP_
#define RCLCPP__GET_MESSAGE_TYPE_SUPPORT_HANDLE_HPP_
#include <type_traits>
#include "rosidl_runtime_cpp/traits.hpp"
#include "rosidl_runtime_cpp/message_type_support_decl.hpp"
#include "rosidl_typesupport_cpp/message_type_support.hpp"
#include "rclcpp/type_adapter.hpp"
/// Versions of rosidl_typesupport_cpp::get_message_type_support_handle that handle adapted types.
namespace rclcpp
{
/// Specialization for when MessageT is an actual ROS message type.
template<typename MessageT>
constexpr
typename std::enable_if_t<
rosidl_generator_traits::is_message<MessageT>::value,
const rosidl_message_type_support_t &
>
get_message_type_support_handle()
{
auto handle = rosidl_typesupport_cpp::get_message_type_support_handle<MessageT>();
if (!handle) {
throw std::runtime_error("Type support handle unexpectedly nullptr");
}
return *handle;
}
/// Specialization for when MessageT is an adapted type using rclcpp::TypeAdapter.
template<typename AdaptedType>
constexpr
typename std::enable_if_t<
!rosidl_generator_traits::is_message<AdaptedType>::value &&
rclcpp::TypeAdapter<AdaptedType>::is_specialized::value,
const rosidl_message_type_support_t &
>
get_message_type_support_handle()
{
auto handle = rosidl_typesupport_cpp::get_message_type_support_handle<
typename TypeAdapter<AdaptedType>::ros_message_type
>();
if (!handle) {
throw std::runtime_error("Type support handle unexpectedly nullptr");
}
return *handle;
}
/// Specialization for when MessageT is not a ROS message nor an adapted type.
/**
* This specialization is a pass through runtime check, which allows a better
* static_assert to catch this issue further down the line.
* This should never get to be called in practice, and is purely defensive.
*/
template<
typename AdaptedType
>
constexpr
typename std::enable_if_t<
!rosidl_generator_traits::is_message<AdaptedType>::value &&
!TypeAdapter<AdaptedType>::is_specialized::value,
const rosidl_message_type_support_t &
>
get_message_type_support_handle()
{
throw std::runtime_error(
"this specialization of rclcpp::get_message_type_support_handle() "
"should never be called");
}
} // namespace rclcpp
#endif // RCLCPP__GET_MESSAGE_TYPE_SUPPORT_HANDLE_HPP_

View File

@@ -0,0 +1,35 @@
// Copyright 2021 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__IS_ROS_COMPATIBLE_TYPE_HPP_
#define RCLCPP__IS_ROS_COMPATIBLE_TYPE_HPP_
#include "rosidl_runtime_cpp/traits.hpp"
#include "rclcpp/type_adapter.hpp"
namespace rclcpp
{
template<typename T>
struct is_ros_compatible_type
{
static constexpr bool value =
rosidl_generator_traits::is_message<T>::value ||
rclcpp::TypeAdapter<T>::is_specialized::value;
};
} // namespace rclcpp
#endif // RCLCPP__IS_ROS_COMPATIBLE_TYPE_HPP_

View File

@@ -103,6 +103,9 @@ public:
* \param[in] allocator Allocator instance in case middleware can not allocate messages
* \throws anything rclcpp::exceptions::throw_from_rcl_error can throw.
*/
[[
deprecated("used the LoanedMessage constructor that does not use a shared_ptr to the allocator")
]]
LoanedMessage(
const rclcpp::PublisherBase * pub,
std::shared_ptr<std::allocator<MessageT>> allocator)

View File

@@ -148,10 +148,16 @@ public:
rclcpp::CallbackGroupType group_type,
bool automatically_add_to_executor_with_node = true);
/// Return the list of callback groups in the node.
/// Iterate over the callback groups in the node, calling the given function on each valid one.
/**
* This method is called in a thread-safe way, and also makes sure to only call the given
* function on those items that are still valid.
*
* \param[in] func The callback function to call on each valid callback group.
*/
RCLCPP_PUBLIC
const std::vector<rclcpp::CallbackGroup::WeakPtr> &
get_callback_groups() const;
void
for_each_callback_group(const node_interfaces::NodeBaseInterface::CallbackGroupFunction & func);
/// Create and return a Publisher.
/**
@@ -206,13 +212,8 @@ public:
typename MessageT,
typename CallbackT,
typename AllocatorT = std::allocator<void>,
typename CallbackMessageT =
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type,
typename SubscriptionT = rclcpp::Subscription<CallbackMessageT, AllocatorT>,
typename MessageMemoryStrategyT = rclcpp::message_memory_strategy::MessageMemoryStrategy<
CallbackMessageT,
AllocatorT
>
typename SubscriptionT = rclcpp::Subscription<MessageT, AllocatorT>,
typename MessageMemoryStrategyT = typename SubscriptionT::MessageMemoryStrategyType
>
std::shared_ptr<SubscriptionT>
create_subscription(
@@ -984,12 +985,15 @@ public:
std::map<std::string, std::vector<std::string>>
get_service_names_and_types() const;
/// Return the number of publishers that are advertised on a given topic.
/// Return a map of existing service names to list of service types for a specific node.
/**
* \param[in] node_name the node_name on which to count the publishers.
* \param[in] namespace_ the namespace of the node associated with the name
* \return number of publishers that are advertised on a given topic.
* \throws std::runtime_error if publishers could not be counted
* This function only considers services - not clients.
* The returned names are the actual names used and do not have remap rules applied.
*
* \param[in] node_name name of the node.
* \param[in] namespace_ namespace of the node.
* \return a map of existing service names to list of service types.
* \throws std::runtime_error anything that rcl_error can throw.
*/
RCLCPP_PUBLIC
std::map<std::string, std::vector<std::string>>

View File

@@ -86,7 +86,6 @@ template<
typename MessageT,
typename CallbackT,
typename AllocatorT,
typename CallbackMessageT,
typename SubscriptionT,
typename MessageMemoryStrategyT>
std::shared_ptr<SubscriptionT>

View File

@@ -15,11 +15,14 @@
#ifndef RCLCPP__NODE_INTERFACES__NODE_BASE_HPP_
#define RCLCPP__NODE_INTERFACES__NODE_BASE_HPP_
#include <functional>
#include <memory>
#include <mutex>
#include <string>
#include <vector>
#include "rcl/node.h"
#include "rclcpp/callback_group.hpp"
#include "rclcpp/context.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
@@ -95,9 +98,16 @@ public:
bool
callback_group_in_node(rclcpp::CallbackGroup::SharedPtr group) override;
/// Iterate over the stored callback groups, calling the given function on each valid one.
/**
* This method is called in a thread-safe way, and also makes sure to only call the given
* function on those items that are still valid.
*
* \param[in] func The callback function to call on each valid callback group.
*/
RCLCPP_PUBLIC
const std::vector<rclcpp::CallbackGroup::WeakPtr> &
get_callback_groups() const override;
void
for_each_callback_group(const CallbackGroupFunction & func) override;
RCLCPP_PUBLIC
std::atomic_bool &
@@ -132,6 +142,7 @@ private:
std::shared_ptr<rcl_node_t> node_handle_;
rclcpp::CallbackGroup::SharedPtr default_callback_group_;
std::mutex callback_groups_mutex_;
std::vector<rclcpp::CallbackGroup::WeakPtr> callback_groups_;
std::atomic_bool associated_with_executor_;

View File

@@ -122,11 +122,19 @@ public:
bool
callback_group_in_node(rclcpp::CallbackGroup::SharedPtr group) = 0;
/// Return list of callback groups associated with this node.
using CallbackGroupFunction = std::function<void (rclcpp::CallbackGroup::SharedPtr)>;
/// Iterate over the stored callback groups, calling the given function on each valid one.
/**
* This method is called in a thread-safe way, and also makes sure to only call the given
* function on those items that are still valid.
*
* \param[in] func The callback function to call on each valid callback group.
*/
RCLCPP_PUBLIC
virtual
const std::vector<rclcpp::CallbackGroup::WeakPtr> &
get_callback_groups() const = 0;
void
for_each_callback_group(const CallbackGroupFunction & func) = 0;
/// Return the atomic bool which is used to ensure only one executor is used.
RCLCPP_PUBLIC

View File

@@ -301,6 +301,16 @@ public:
return get<ParameterType::PARAMETER_BOOL_ARRAY>();
}
template<typename type>
constexpr
typename std::enable_if<
std::is_convertible<
type, const std::vector<int> &>::value, const std::vector<int64_t> &>::type
get() const
{
return get<ParameterType::PARAMETER_INTEGER_ARRAY>();
}
template<typename type>
constexpr
typename std::enable_if<
@@ -311,6 +321,16 @@ public:
return get<ParameterType::PARAMETER_INTEGER_ARRAY>();
}
template<typename type>
constexpr
typename std::enable_if<
std::is_convertible<
type, const std::vector<float> &>::value, const std::vector<double> &>::type
get() const
{
return get<ParameterType::PARAMETER_DOUBLE_ARRAY>();
}
template<typename type>
constexpr
typename std::enable_if<

View File

@@ -15,9 +15,6 @@
#ifndef RCLCPP__PUBLISHER_HPP_
#define RCLCPP__PUBLISHER_HPP_
#include <rmw/error_handling.h>
#include <rmw/rmw.h>
#include <functional>
#include <iostream>
#include <memory>
@@ -27,16 +24,21 @@
#include "rcl/error_handling.h"
#include "rcl/publisher.h"
#include "rmw/error_handling.h"
#include "rmw/rmw.h"
#include "rclcpp/allocator/allocator_common.hpp"
#include "rclcpp/allocator/allocator_deleter.hpp"
#include "rclcpp/detail/resolve_use_intra_process.hpp"
#include "rclcpp/experimental/intra_process_manager.hpp"
#include "rclcpp/get_message_type_support_handle.hpp"
#include "rclcpp/is_ros_compatible_type.hpp"
#include "rclcpp/loaned_message.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/publisher_base.hpp"
#include "rclcpp/publisher_options.hpp"
#include "rclcpp/type_adapter.hpp"
#include "rclcpp/type_support_decl.hpp"
#include "rclcpp/visibility_control.hpp"
@@ -49,15 +51,62 @@ template<typename MessageT, typename AllocatorT>
class LoanedMessage;
/// A publisher publishes messages of any type to a topic.
/**
* MessageT must be a:
*
* - ROS message type with its own type support (e.g. std_msgs::msgs::String), or a
* - rclcpp::TypeAdapter<CustomType, ROSMessageType>
* (e.g. rclcpp::TypeAdapter<std::string, std_msgs::msg::String), or a
* - custom type that has been setup as the implicit type for a ROS type using
* RCLCPP_USING_CUSTOM_TYPE_AS_ROS_MESSAGE_TYPE(custom_type, ros_message_type)
*
* In the case that MessageT is ROS message type (e.g. std_msgs::msg::String),
* both PublishedType and ROSMessageType will be that type.
* In the case that MessageT is a TypeAdapter<CustomType, ROSMessageType> type
* (e.g. TypeAdapter<std::string, std_msgs::msg::String>), PublishedType will
* be the custom type, and ROSMessageType will be the ros message type.
*
* This is achieved because of the "identity specialization" for TypeAdapter,
* which returns itself if it is already a TypeAdapter, and the default
* specialization which allows ROSMessageType to be void.
* \sa rclcpp::TypeAdapter for more details.
*/
template<typename MessageT, typename AllocatorT = std::allocator<void>>
class Publisher : public PublisherBase
{
public:
using MessageAllocatorTraits = allocator::AllocRebind<MessageT, AllocatorT>;
using MessageAllocator = typename MessageAllocatorTraits::allocator_type;
using MessageDeleter = allocator::Deleter<MessageAllocator, MessageT>;
using MessageUniquePtr = std::unique_ptr<MessageT, MessageDeleter>;
using MessageSharedPtr = std::shared_ptr<const MessageT>;
static_assert(
rclcpp::is_ros_compatible_type<MessageT>::value,
"given message type is not compatible with ROS and cannot be used with a Publisher");
/// MessageT::custom_type if MessageT is a TypeAdapter, otherwise just MessageT.
using PublishedType = typename rclcpp::TypeAdapter<MessageT>::custom_type;
/// MessageT::ros_message_type if MessageT is a TypeAdapter, otherwise just MessageT.
using ROSMessageType = typename rclcpp::TypeAdapter<MessageT>::ros_message_type;
using PublishedTypeAllocatorTraits = allocator::AllocRebind<PublishedType, AllocatorT>;
using PublishedTypeAllocator = typename PublishedTypeAllocatorTraits::allocator_type;
using PublishedTypeDeleter = allocator::Deleter<PublishedTypeAllocator, PublishedType>;
using ROSMessageTypeAllocatorTraits = allocator::AllocRebind<ROSMessageType, AllocatorT>;
using ROSMessageTypeAllocator = typename ROSMessageTypeAllocatorTraits::allocator_type;
using ROSMessageTypeDeleter = allocator::Deleter<ROSMessageTypeAllocator, ROSMessageType>;
using MessageAllocatorTraits
[[deprecated("use PublishedTypeAllocatorTraits")]] =
PublishedTypeAllocatorTraits;
using MessageAllocator
[[deprecated("use PublishedTypeAllocator")]] =
PublishedTypeAllocator;
using MessageDeleter
[[deprecated("use PublishedTypeDeleter")]] =
PublishedTypeDeleter;
using MessageUniquePtr
[[deprecated("use std::unique_ptr<PublishedType, PublishedTypeDeleter>")]] =
std::unique_ptr<PublishedType, PublishedTypeDeleter>;
using MessageSharedPtr
[[deprecated("use std::shared_ptr<const PublishedType>")]] =
std::shared_ptr<const PublishedType>;
RCLCPP_SMART_PTR_DEFINITIONS(Publisher<MessageT, AllocatorT>)
@@ -80,12 +129,14 @@ public:
: PublisherBase(
node_base,
topic,
*rosidl_typesupport_cpp::get_message_type_support_handle<MessageT>(),
rclcpp::get_message_type_support_handle<MessageT>(),
options.template to_rcl_publisher_options<MessageT>(qos)),
options_(options),
message_allocator_(new MessageAllocator(*options.get_allocator().get()))
published_type_allocator_(*options.get_allocator()),
ros_message_type_allocator_(*options.get_allocator())
{
allocator::set_allocator_for_deleter(&message_deleter_, message_allocator_.get());
allocator::set_allocator_for_deleter(&published_type_deleter_, &published_type_allocator_);
allocator::set_allocator_for_deleter(&ros_message_type_deleter_, &ros_message_type_allocator_);
if (options_.event_callbacks.deadline_callback) {
this->add_event_handler(
@@ -135,15 +186,15 @@ public:
// Get the intra process manager instance for this context.
auto ipm = context->get_sub_context<rclcpp::experimental::IntraProcessManager>();
// Register the publisher with the intra process manager.
if (qos.get_rmw_qos_profile().history == RMW_QOS_POLICY_HISTORY_KEEP_ALL) {
if (qos.history() != rclcpp::HistoryPolicy::KeepLast) {
throw std::invalid_argument(
"intraprocess communication is not allowed with keep all history qos policy");
"intraprocess communication allowed only with keep last history qos policy");
}
if (qos.get_rmw_qos_profile().depth == 0) {
if (qos.depth() == 0) {
throw std::invalid_argument(
"intraprocess communication is not allowed with a zero qos history depth value");
}
if (qos.get_rmw_qos_profile().durability != RMW_QOS_POLICY_DURABILITY_VOLATILE) {
if (qos.durability() != rclcpp::DurabilityPolicy::Volatile) {
throw std::invalid_argument(
"intraprocess communication allowed only with volatile durability");
}
@@ -170,21 +221,33 @@ public:
* allocator.
* \sa rclcpp::LoanedMessage for details of the LoanedMessage class.
*
* \return LoanedMessage containing memory for a ROS message of type MessageT
* \return LoanedMessage containing memory for a ROS message of type ROSMessageType
*/
rclcpp::LoanedMessage<MessageT, AllocatorT>
rclcpp::LoanedMessage<ROSMessageType, AllocatorT>
borrow_loaned_message()
{
return rclcpp::LoanedMessage<MessageT, AllocatorT>(this, this->get_allocator());
return rclcpp::LoanedMessage<ROSMessageType, AllocatorT>(
*this,
this->get_ros_message_type_allocator());
}
/// Send a message to the topic for this publisher.
/// Publish a message on the topic.
/**
* This function is templated on the input message type, MessageT.
* \param[in] msg A shared pointer to the message to send.
* This signature is enabled if the element_type of the std::unique_ptr is
* a ROS message type, as opposed to the custom_type of a TypeAdapter, and
* that type matches the type given when creating the publisher.
*
* This signature allows the user to give ownership of the message to rclcpp,
* allowing for more efficient intra-process communication optimizations.
*
* \param[in] msg A unique pointer to the message to send.
*/
virtual void
publish(std::unique_ptr<MessageT, MessageDeleter> msg)
template<typename T>
typename std::enable_if_t<
rosidl_generator_traits::is_message<T>::value &&
std::is_same<T, ROSMessageType>::value
>
publish(std::unique_ptr<T, ROSMessageTypeDeleter> msg)
{
if (!intra_process_is_enabled_) {
this->do_inter_process_publish(*msg);
@@ -207,8 +270,24 @@ public:
}
}
virtual void
publish(const MessageT & msg)
/// Publish a message on the topic.
/**
* This signature is enabled if the object being published is
* a ROS message type, as opposed to the custom_type of a TypeAdapter, and
* that type matches the type given when creating the publisher.
*
* This signature allows the user to give a reference to a message, which is
* copied onto the heap without modification so that a copy can be owned by
* rclcpp and ownership of the copy can be moved later if needed.
*
* \param[in] msg A const reference to the message to send.
*/
template<typename T>
typename std::enable_if_t<
rosidl_generator_traits::is_message<T>::value &&
std::is_same<T, ROSMessageType>::value
>
publish(const T & msg)
{
// Avoid allocating when not using intra process.
if (!intra_process_is_enabled_) {
@@ -218,12 +297,77 @@ public:
// Otherwise we have to allocate memory in a unique_ptr and pass it along.
// As the message is not const, a copy should be made.
// A shared_ptr<const MessageT> could also be constructed here.
auto ptr = MessageAllocatorTraits::allocate(*message_allocator_.get(), 1);
MessageAllocatorTraits::construct(*message_allocator_.get(), ptr, msg);
MessageUniquePtr unique_msg(ptr, message_deleter_);
auto unique_msg = this->duplicate_ros_message_as_unique_ptr(msg);
this->publish(std::move(unique_msg));
}
/// Publish a message on the topic.
/**
* This signature is enabled if this class was created with a TypeAdapter and
* the element_type of the std::unique_ptr matches the custom_type for the
* TypeAdapter used with this class.
*
* This signature allows the user to give ownership of the message to rclcpp,
* allowing for more efficient intra-process communication optimizations.
*
* \param[in] msg A unique pointer to the message to send.
*/
template<typename T>
typename std::enable_if_t<
rclcpp::TypeAdapter<MessageT>::is_specialized::value &&
std::is_same<T, PublishedType>::value
>
publish(std::unique_ptr<T, PublishedTypeDeleter> msg)
{
// TODO(wjwwood): later update this to give the unique_ptr to the intra
// process manager and let it decide if it needs to be converted or not.
// For now, convert it unconditionally and pass it the ROSMessageType
// publish function specialization.
auto unique_ros_msg = this->create_ros_message_unique_ptr();
rclcpp::TypeAdapter<MessageT>::convert_to_ros_message(*msg, *unique_ros_msg);
this->publish(std::move(unique_ros_msg));
}
/// Publish a message on the topic.
/**
* This signature is enabled if this class was created with a TypeAdapter and
* the given type matches the custom_type of the TypeAdapter.
*
* This signature allows the user to give a reference to a message, which is
* copied onto the heap without modification so that a copy can be owned by
* rclcpp and ownership of the copy can be moved later if needed.
*
* \param[in] msg A const reference to the message to send.
*/
template<typename T>
typename std::enable_if_t<
rclcpp::TypeAdapter<MessageT>::is_specialized::value &&
std::is_same<T, PublishedType>::value
>
publish(const T & msg)
{
// TODO(wjwwood): later update this to give the unique_ptr to the intra
// process manager and let it decide if it needs to be converted or not.
// For now, convert it unconditionally and pass it the ROSMessageType
// publish function specialization.
// Avoid allocating when not using intra process.
if (!intra_process_is_enabled_) {
// Convert to the ROS message equivalent and publish it.
ROSMessageType ros_msg;
rclcpp::TypeAdapter<MessageT>::convert_to_ros_message(msg, ros_msg);
// In this case we're not using intra process.
return this->do_inter_process_publish(ros_msg);
}
// Otherwise we have to allocate memory in a unique_ptr, convert it,
// and pass it along.
// As the message is not const, a copy should be made.
// A shared_ptr<const MessageT> could also be constructed here.
auto unique_ros_msg = this->create_ros_message_unique_ptr();
rclcpp::TypeAdapter<MessageT>::convert_to_ros_message(msg, *unique_ros_msg);
this->publish(std::move(unique_ros_msg));
}
void
publish(const rcl_serialized_message_t & serialized_msg)
{
@@ -245,7 +389,7 @@ public:
* \param loaned_msg The LoanedMessage instance to be published.
*/
void
publish(rclcpp::LoanedMessage<MessageT, AllocatorT> && loaned_msg)
publish(rclcpp::LoanedMessage<ROSMessageType, AllocatorT> && loaned_msg)
{
if (!loaned_msg.is_valid()) {
throw std::runtime_error("loaned message is not valid");
@@ -271,20 +415,30 @@ public:
}
}
std::shared_ptr<MessageAllocator>
[[deprecated("use get_published_type_allocator() or get_ros_message_type_allocator() instead")]]
std::shared_ptr<PublishedTypeAllocator>
get_allocator() const
{
return message_allocator_;
return std::make_shared<PublishedTypeAllocator>(published_type_allocator_);
}
PublishedTypeAllocator
get_published_type_allocator() const
{
return published_type_allocator_;
}
ROSMessageTypeAllocator
get_ros_message_type_allocator() const
{
return ros_message_type_allocator_;
}
protected:
void
do_inter_process_publish(const MessageT & msg)
do_inter_process_publish(const ROSMessageType & msg)
{
TRACEPOINT(
rclcpp_publish,
static_cast<const void *>(publisher_handle_.get()),
static_cast<const void *>(&msg));
TRACEPOINT(rclcpp_publish, nullptr, static_cast<const void *>(&msg));
auto status = rcl_publish(publisher_handle_.get(), &msg, nullptr);
if (RCL_RET_PUBLISHER_INVALID == status) {
@@ -316,7 +470,8 @@ protected:
}
void
do_loaned_message_publish(std::unique_ptr<MessageT, std::function<void(MessageT *)>> msg)
do_loaned_message_publish(
std::unique_ptr<ROSMessageType, std::function<void(ROSMessageType *)>> msg)
{
auto status = rcl_publish_loaned_message(publisher_handle_.get(), msg.get(), nullptr);
@@ -336,7 +491,7 @@ protected:
}
void
do_intra_process_publish(std::unique_ptr<MessageT, MessageDeleter> msg)
do_intra_process_publish(std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter> msg)
{
auto ipm = weak_ipm_.lock();
if (!ipm) {
@@ -347,14 +502,15 @@ protected:
throw std::runtime_error("cannot publish msg which is a null pointer");
}
ipm->template do_intra_process_publish<MessageT, AllocatorT>(
ipm->template do_intra_process_publish<ROSMessageType, AllocatorT>(
intra_process_publisher_id_,
std::move(msg),
message_allocator_);
ros_message_type_allocator_);
}
std::shared_ptr<const MessageT>
do_intra_process_publish_and_return_shared(std::unique_ptr<MessageT, MessageDeleter> msg)
std::shared_ptr<const ROSMessageType>
do_intra_process_publish_and_return_shared(
std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter> msg)
{
auto ipm = weak_ipm_.lock();
if (!ipm) {
@@ -365,10 +521,29 @@ protected:
throw std::runtime_error("cannot publish msg which is a null pointer");
}
return ipm->template do_intra_process_publish_and_return_shared<MessageT, AllocatorT>(
return ipm->template do_intra_process_publish_and_return_shared<ROSMessageType,
AllocatorT>(
intra_process_publisher_id_,
std::move(msg),
message_allocator_);
ros_message_type_allocator_);
}
/// Return a new unique_ptr using the ROSMessageType of the publisher.
std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter>
create_ros_message_unique_ptr()
{
auto ptr = ROSMessageTypeAllocatorTraits::allocate(ros_message_type_allocator_, 1);
ROSMessageTypeAllocatorTraits::construct(ros_message_type_allocator_, ptr);
return std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter>(ptr, ros_message_type_deleter_);
}
/// Duplicate a given ros message as a unique_ptr.
std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter>
duplicate_ros_message_as_unique_ptr(const ROSMessageType & msg)
{
auto ptr = ROSMessageTypeAllocatorTraits::allocate(ros_message_type_allocator_, 1);
ROSMessageTypeAllocatorTraits::construct(ros_message_type_allocator_, ptr, msg);
return std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter>(ptr, ros_message_type_deleter_);
}
/// Copy of original options passed during construction.
@@ -378,9 +553,10 @@ protected:
*/
const rclcpp::PublisherOptionsWithAllocator<AllocatorT> options_;
std::shared_ptr<MessageAllocator> message_allocator_;
MessageDeleter message_deleter_;
PublishedTypeAllocator published_type_allocator_;
PublishedTypeDeleter published_type_deleter_;
ROSMessageTypeAllocator ros_message_type_allocator_;
ROSMessageTypeDeleter ros_message_type_deleter_;
};
} // namespace rclcpp

View File

@@ -17,6 +17,7 @@
#include <memory>
#include <string>
#include <type_traits>
#include <vector>
#include "rcl/publisher.h"
@@ -64,6 +65,10 @@ struct PublisherOptionsBase
template<typename Allocator>
struct PublisherOptionsWithAllocator : public PublisherOptionsBase
{
static_assert(
std::is_void_v<typename std::allocator_traits<Allocator>::value_type>,
"Publisher allocator value type must be void");
/// Optional custom allocator.
std::shared_ptr<Allocator> allocator = nullptr;
@@ -80,10 +85,7 @@ struct PublisherOptionsWithAllocator : public PublisherOptionsBase
to_rcl_publisher_options(const rclcpp::QoS & qos) const
{
rcl_publisher_options_t result = rcl_publisher_get_default_options();
using AllocatorTraits = std::allocator_traits<Allocator>;
using MessageAllocatorT = typename AllocatorTraits::template rebind_alloc<MessageT>;
auto message_alloc = std::make_shared<MessageAllocatorT>(*this->get_allocator().get());
result.allocator = rclcpp::allocator::get_rcl_allocator<MessageT>(*message_alloc);
result.allocator = this->get_rcl_allocator();
result.qos = qos.get_rmw_qos_profile();
result.rmw_publisher_options.require_unique_network_flow_endpoints =
this->require_unique_network_flow_endpoints;
@@ -102,10 +104,35 @@ struct PublisherOptionsWithAllocator : public PublisherOptionsBase
get_allocator() const
{
if (!this->allocator) {
return std::make_shared<Allocator>();
if (!allocator_storage_) {
allocator_storage_ = std::make_shared<Allocator>();
}
return allocator_storage_;
}
return this->allocator;
}
private:
using PlainAllocator =
typename std::allocator_traits<Allocator>::template rebind_alloc<char>;
rcl_allocator_t
get_rcl_allocator() const
{
if (!plain_allocator_storage_) {
plain_allocator_storage_ =
std::make_shared<PlainAllocator>(*this->get_allocator());
}
return rclcpp::allocator::get_rcl_allocator<char>(*plain_allocator_storage_);
}
// This is a temporal workaround, to make sure that get_allocator()
// always returns a copy of the same allocator.
mutable std::shared_ptr<Allocator> allocator_storage_;
// This is a temporal workaround, to keep the plain allocator that backs
// up the rcl allocator returned in rcl_publisher_options_t alive.
mutable std::shared_ptr<PlainAllocator> plain_allocator_storage_;
};
using PublisherOptions = PublisherOptionsWithAllocator<std::allocator<void>>;

View File

@@ -140,7 +140,6 @@
* - rclcpp/duration.hpp
* - rclcpp/function_traits.hpp
* - rclcpp/macros.hpp
* - rclcpp/scope_exit.hpp
* - rclcpp/time.hpp
* - rclcpp/utilities.hpp
* - rclcpp/typesupport_helpers.hpp

View File

@@ -19,6 +19,10 @@
#ifndef RCLCPP__SCOPE_EXIT_HPP_
#define RCLCPP__SCOPE_EXIT_HPP_
// TODO(christophebedard) remove this header completely in I-turtle
#warning rclcpp/scope_exit.hpp has been deprecated, please use rcpputils/scope_exit.hpp instead
#include <functional>
#include "rclcpp/macros.hpp"

View File

@@ -47,7 +47,7 @@ public:
* \param[in] initial_capacity The amount of memory to be allocated.
* \param[in] allocator The allocator to be used for the initialization.
*/
SerializedMessage(
explicit SerializedMessage(
size_t initial_capacity,
const rcl_allocator_t & allocator = rcl_get_default_allocator());

View File

@@ -141,7 +141,9 @@ protected:
};
template<typename ServiceT>
class Service : public ServiceBase
class Service
: public ServiceBase,
public std::enable_shared_from_this<Service<ServiceT>>
{
public:
using CallbackType = std::function<
@@ -177,25 +179,16 @@ public:
using rosidl_typesupport_cpp::get_service_type_support_handle;
auto service_type_support_handle = get_service_type_support_handle<ServiceT>();
std::weak_ptr<rcl_node_t> weak_node_handle(node_handle_);
// rcl does the static memory allocation here
service_handle_ = std::shared_ptr<rcl_service_t>(
new rcl_service_t, [weak_node_handle, service_name](rcl_service_t * service)
new rcl_service_t, [handle = node_handle_, service_name](rcl_service_t * service)
{
auto handle = weak_node_handle.lock();
if (handle) {
if (rcl_service_fini(service, handle.get()) != RCL_RET_OK) {
RCLCPP_ERROR(
rclcpp::get_node_logger(handle.get()).get_child("rclcpp"),
"Error in destruction of rcl service handle: %s",
rcl_get_error_string().str);
rcl_reset_error();
}
} else {
RCLCPP_ERROR_STREAM(
rclcpp::get_logger("rclcpp"),
"Error in destruction of rcl service handle " << service_name <<
": the Node Handle was destructed too early. You will leak memory");
if (rcl_service_fini(service, handle.get()) != RCL_RET_OK) {
RCLCPP_ERROR(
rclcpp::get_node_logger(handle.get()).get_child("rclcpp"),
"Error in destruction of rcl service handle: %s",
rcl_get_error_string().str);
rcl_reset_error();
}
delete service;
});
@@ -344,9 +337,10 @@ public:
std::shared_ptr<void> request) override
{
auto typed_request = std::static_pointer_cast<typename ServiceT::Request>(request);
auto response = std::make_shared<typename ServiceT::Response>();
any_callback_.dispatch(request_header, typed_request, response);
send_response(*request_header, *response);
auto response = any_callback_.dispatch(this->shared_from_this(), request_header, typed_request);
if (response) {
send_response(*request_header, *response);
}
}
void

View File

@@ -388,6 +388,11 @@ public:
++it;
continue;
}
if (!timer->call()) {
// timer was cancelled, skip it.
++it;
continue;
}
// Otherwise it is safe to set and return the any_exec
any_exec.timer = timer;
any_exec.callback_group = group;
@@ -395,7 +400,7 @@ public:
timer_handles_.erase(it);
return;
}
// Else, the service is no longer valid, remove it and continue
// Else, the timer is no longer valid, remove it and continue
it = timer_handles_.erase(it);
}
}

View File

@@ -60,10 +60,16 @@ class NodeTopicsInterface;
/// Subscription implementation, templated on the type of message this subscription receives.
template<
typename CallbackMessageT,
typename MessageT,
typename AllocatorT = std::allocator<void>,
/// MessageT::custom_type if MessageT is a TypeAdapter,
/// otherwise just MessageT.
typename SubscribedT = typename rclcpp::TypeAdapter<MessageT>::custom_type,
/// MessageT::ros_message_type if MessageT is a TypeAdapter,
/// otherwise just MessageT.
typename ROSMessageT = typename rclcpp::TypeAdapter<MessageT>::ros_message_type,
typename MessageMemoryStrategyT = rclcpp::message_memory_strategy::MessageMemoryStrategy<
CallbackMessageT,
ROSMessageT,
AllocatorT
>>
class Subscription : public SubscriptionBase
@@ -71,14 +77,36 @@ class Subscription : public SubscriptionBase
friend class rclcpp::node_interfaces::NodeTopicsInterface;
public:
using MessageAllocatorTraits = allocator::AllocRebind<CallbackMessageT, AllocatorT>;
using MessageAllocator = typename MessageAllocatorTraits::allocator_type;
using MessageDeleter = allocator::Deleter<MessageAllocator, CallbackMessageT>;
using ConstMessageSharedPtr = std::shared_ptr<const CallbackMessageT>;
using MessageUniquePtr = std::unique_ptr<CallbackMessageT, MessageDeleter>;
using SubscriptionTopicStatisticsSharedPtr =
std::shared_ptr<rclcpp::topic_statistics::SubscriptionTopicStatistics<CallbackMessageT>>;
// Redeclare these here to use outside of the class.
using SubscribedType = SubscribedT;
using ROSMessageType = ROSMessageT;
using MessageMemoryStrategyType = MessageMemoryStrategyT;
using SubscribedTypeAllocatorTraits = allocator::AllocRebind<SubscribedType, AllocatorT>;
using SubscribedTypeAllocator = typename SubscribedTypeAllocatorTraits::allocator_type;
using SubscribedTypeDeleter = allocator::Deleter<SubscribedTypeAllocator, SubscribedType>;
using ROSMessageTypeAllocatorTraits = allocator::AllocRebind<ROSMessageType, AllocatorT>;
using ROSMessageTypeAllocator = typename ROSMessageTypeAllocatorTraits::allocator_type;
using ROSMessageTypeDeleter = allocator::Deleter<ROSMessageTypeAllocator, ROSMessageType>;
using MessageAllocatorTraits [[deprecated("use ROSMessageTypeAllocatorTraits")]] =
ROSMessageTypeAllocatorTraits;
using MessageAllocator [[deprecated("use ROSMessageTypeAllocator")]] =
ROSMessageTypeAllocator;
using MessageDeleter [[deprecated("use ROSMessageTypeDeleter")]] =
ROSMessageTypeDeleter;
using ConstMessageSharedPtr [[deprecated]] = std::shared_ptr<const ROSMessageType>;
using MessageUniquePtr
[[deprecated("use std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter> instead")]] =
std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter>;
private:
using SubscriptionTopicStatisticsSharedPtr =
std::shared_ptr<rclcpp::topic_statistics::SubscriptionTopicStatistics<ROSMessageType>>;
public:
RCLCPP_SMART_PTR_DEFINITIONS(Subscription)
/// Default constructor.
@@ -104,7 +132,7 @@ public:
const rosidl_message_type_support_t & type_support_handle,
const std::string & topic_name,
const rclcpp::QoS & qos,
AnySubscriptionCallback<CallbackMessageT, AllocatorT> callback,
AnySubscriptionCallback<MessageT, AllocatorT> callback,
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options,
typename MessageMemoryStrategyT::SharedPtr message_memory_strategy,
SubscriptionTopicStatisticsSharedPtr subscription_topic_statistics = nullptr)
@@ -112,8 +140,8 @@ public:
node_base,
type_support_handle,
topic_name,
options.template to_rcl_subscription_options<CallbackMessageT>(qos),
rclcpp::subscription_traits::is_serialized_subscription_argument<CallbackMessageT>::value),
options.template to_rcl_subscription_options<ROSMessageType>(qos),
callback.is_serialized_message_callback()),
any_callback_(callback),
options_(options),
message_memory_strategy_(message_memory_strategy)
@@ -155,16 +183,16 @@ public:
using rclcpp::detail::resolve_intra_process_buffer_type;
// Check if the QoS is compatible with intra-process.
rmw_qos_profile_t qos_profile = get_actual_qos().get_rmw_qos_profile();
if (qos_profile.history == RMW_QOS_POLICY_HISTORY_KEEP_ALL) {
auto qos_profile = get_actual_qos();
if (qos_profile.history() != rclcpp::HistoryPolicy::KeepLast) {
throw std::invalid_argument(
"intraprocess communication is not allowed with keep all history qos policy");
"intraprocess communication allowed only with keep last history qos policy");
}
if (qos_profile.depth == 0) {
if (qos_profile.depth() == 0) {
throw std::invalid_argument(
"intraprocess communication is not allowed with 0 depth qos policy");
}
if (qos_profile.durability != RMW_QOS_POLICY_DURABILITY_VOLATILE) {
if (qos_profile.durability() != rclcpp::DurabilityPolicy::Volatile) {
throw std::invalid_argument(
"intraprocess communication allowed only with volatile durability");
}
@@ -241,11 +269,34 @@ public:
* \throws any rcl errors from rcl_take, \sa rclcpp::exceptions::throw_from_rcl_error()
*/
bool
take(CallbackMessageT & message_out, rclcpp::MessageInfo & message_info_out)
take(ROSMessageType & message_out, rclcpp::MessageInfo & message_info_out)
{
return this->take_type_erased(static_cast<void *>(&message_out), message_info_out);
}
/// Take the next message from the inter-process subscription.
/**
* This verison takes a SubscribedType which is different frmo the
* ROSMessageType when a rclcpp::TypeAdapter is in used.
*
* \sa take(ROSMessageType &, rclcpp::MessageInfo &)
*/
template<typename TakeT>
std::enable_if_t<
!rosidl_generator_traits::is_message<TakeT>::value &&
std::is_same_v<TakeT, SubscribedType>,
bool
>
take(TakeT & message_out, rclcpp::MessageInfo & message_info_out)
{
ROSMessageType local_message;
bool taken = this->take_type_erased(static_cast<void *>(&local_message), message_info_out);
if (taken) {
rclcpp::TypeAdapter<MessageT>::convert_to_custom(local_message, message_out);
}
return taken;
}
std::shared_ptr<void>
create_message() override
{
@@ -272,7 +323,7 @@ public:
// we should ignore this copy of the message.
return;
}
auto typed_message = std::static_pointer_cast<CallbackMessageT>(message);
auto typed_message = std::static_pointer_cast<ROSMessageType>(message);
std::chrono::time_point<std::chrono::system_clock> now;
if (subscription_topic_statistics_) {
@@ -290,15 +341,24 @@ public:
}
}
void
handle_serialized_message(
const std::shared_ptr<rclcpp::SerializedMessage> & serialized_message,
const rclcpp::MessageInfo & message_info) override
{
// TODO(wjwwood): enable topic statistics for serialized messages
any_callback_.dispatch(serialized_message, message_info);
}
void
handle_loaned_message(
void * loaned_message,
const rclcpp::MessageInfo & message_info) override
{
auto typed_message = static_cast<CallbackMessageT *>(loaned_message);
auto typed_message = static_cast<ROSMessageType *>(loaned_message);
// message is loaned, so we have to make sure that the deleter does not deallocate the message
auto sptr = std::shared_ptr<CallbackMessageT>(
typed_message, [](CallbackMessageT * msg) {(void) msg;});
auto sptr = std::shared_ptr<ROSMessageType>(
typed_message, [](ROSMessageType * msg) {(void) msg;});
any_callback_.dispatch(sptr, message_info);
}
@@ -309,7 +369,7 @@ public:
void
return_message(std::shared_ptr<void> & message) override
{
auto typed_message = std::static_pointer_cast<CallbackMessageT>(message);
auto typed_message = std::static_pointer_cast<ROSMessageType>(message);
message_memory_strategy_->return_message(typed_message);
}
@@ -332,21 +392,24 @@ public:
private:
RCLCPP_DISABLE_COPY(Subscription)
AnySubscriptionCallback<CallbackMessageT, AllocatorT> any_callback_;
AnySubscriptionCallback<MessageT, AllocatorT> any_callback_;
/// Copy of original options passed during construction.
/**
* It is important to save a copy of this so that the rmw payload which it
* may contain is kept alive for the duration of the subscription.
*/
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> options_;
typename message_memory_strategy::MessageMemoryStrategy<CallbackMessageT, AllocatorT>::SharedPtr
typename message_memory_strategy::MessageMemoryStrategy<ROSMessageType, AllocatorT>::SharedPtr
message_memory_strategy_;
/// Component which computes and publishes topic statistics for this subscriber
SubscriptionTopicStatisticsSharedPtr subscription_topic_statistics_{nullptr};
using SubscriptionIntraProcessT = rclcpp::experimental::SubscriptionIntraProcess<
CallbackMessageT,
ROSMessageType,
AllocatorT,
typename MessageUniquePtr::deleter_type>;
ROSMessageTypeDeleter,
MessageT>;
std::shared_ptr<SubscriptionIntraProcessT> subscription_intra_process_;
};

View File

@@ -183,6 +183,13 @@ public:
void
handle_message(std::shared_ptr<void> & message, const rclcpp::MessageInfo & message_info) = 0;
RCLCPP_PUBLIC
virtual
void
handle_serialized_message(
const std::shared_ptr<rclcpp::SerializedMessage> & serialized_message,
const rclcpp::MessageInfo & message_info) = 0;
RCLCPP_PUBLIC
virtual
void

View File

@@ -25,6 +25,7 @@
#include "rosidl_typesupport_cpp/message_type_support.hpp"
#include "rclcpp/any_subscription_callback.hpp"
#include "rclcpp/get_message_type_support_handle.hpp"
#include "rclcpp/intra_process_buffer_type.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/qos.hpp"
@@ -74,26 +75,23 @@ template<
typename MessageT,
typename CallbackT,
typename AllocatorT,
typename CallbackMessageT =
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type,
typename SubscriptionT = rclcpp::Subscription<CallbackMessageT, AllocatorT>,
typename MessageMemoryStrategyT = rclcpp::message_memory_strategy::MessageMemoryStrategy<
CallbackMessageT,
AllocatorT
>>
typename SubscriptionT = rclcpp::Subscription<MessageT, AllocatorT>,
typename MessageMemoryStrategyT = typename SubscriptionT::MessageMemoryStrategyType,
typename ROSMessageType = typename SubscriptionT::ROSMessageType
>
SubscriptionFactory
create_subscription_factory(
CallbackT && callback,
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options,
typename MessageMemoryStrategyT::SharedPtr msg_mem_strat,
std::shared_ptr<rclcpp::topic_statistics::SubscriptionTopicStatistics<CallbackMessageT>>
std::shared_ptr<rclcpp::topic_statistics::SubscriptionTopicStatistics<ROSMessageType>>
subscription_topic_stats = nullptr
)
{
auto allocator = options.get_allocator();
using rclcpp::AnySubscriptionCallback;
AnySubscriptionCallback<CallbackMessageT, AllocatorT> any_subscription_callback(*allocator);
AnySubscriptionCallback<MessageT, AllocatorT> any_subscription_callback(*allocator);
any_subscription_callback.set(std::forward<CallbackT>(callback));
SubscriptionFactory factory {
@@ -107,9 +105,9 @@ create_subscription_factory(
using rclcpp::Subscription;
using rclcpp::SubscriptionBase;
auto sub = Subscription<CallbackMessageT, AllocatorT>::make_shared(
auto sub = Subscription<MessageT, AllocatorT>::make_shared(
node_base,
*rosidl_typesupport_cpp::get_message_type_support_handle<MessageT>(),
rclcpp::get_message_type_support_handle<MessageT>(),
topic_name,
qos,
any_subscription_callback,

View File

@@ -18,6 +18,7 @@
#include <chrono>
#include <memory>
#include <string>
#include <type_traits>
#include <vector>
#include "rclcpp/callback_group.hpp"
@@ -86,6 +87,10 @@ struct SubscriptionOptionsBase
template<typename Allocator>
struct SubscriptionOptionsWithAllocator : public SubscriptionOptionsBase
{
static_assert(
std::is_void_v<typename std::allocator_traits<Allocator>::value_type>,
"Subscription allocator value type must be void");
/// Optional custom allocator.
std::shared_ptr<Allocator> allocator = nullptr;
@@ -107,10 +112,7 @@ struct SubscriptionOptionsWithAllocator : public SubscriptionOptionsBase
to_rcl_subscription_options(const rclcpp::QoS & qos) const
{
rcl_subscription_options_t result = rcl_subscription_get_default_options();
using AllocatorTraits = std::allocator_traits<Allocator>;
using MessageAllocatorT = typename AllocatorTraits::template rebind_alloc<MessageT>;
auto message_alloc = std::make_shared<MessageAllocatorT>(*this->get_allocator().get());
result.allocator = allocator::get_rcl_allocator<MessageT>(*message_alloc);
result.allocator = this->get_rcl_allocator();
result.qos = qos.get_rmw_qos_profile();
result.rmw_subscription_options.ignore_local_publications = this->ignore_local_publications;
result.rmw_subscription_options.require_unique_network_flow_endpoints =
@@ -124,15 +126,39 @@ struct SubscriptionOptionsWithAllocator : public SubscriptionOptionsBase
return result;
}
/// Get the allocator, creating one if needed.
std::shared_ptr<Allocator>
get_allocator() const
{
if (!this->allocator) {
return std::make_shared<Allocator>();
if (!allocator_storage_) {
allocator_storage_ = std::make_shared<Allocator>();
}
return allocator_storage_;
}
return this->allocator;
}
private:
using PlainAllocator =
typename std::allocator_traits<Allocator>::template rebind_alloc<char>;
rcl_allocator_t
get_rcl_allocator() const
{
if (!plain_allocator_storage_) {
plain_allocator_storage_ =
std::make_shared<PlainAllocator>(*this->get_allocator());
}
return rclcpp::allocator::get_rcl_allocator<char>(*plain_allocator_storage_);
}
// This is a temporal workaround, to make sure that get_allocator()
// always returns a copy of the same allocator.
mutable std::shared_ptr<Allocator> allocator_storage_;
// This is a temporal workaround, to keep the plain allocator that backs
// up the rcl allocator returned in rcl_subscription_options_t alive.
mutable std::shared_ptr<PlainAllocator> plain_allocator_storage_;
};
using SubscriptionOptions = SubscriptionOptionsWithAllocator<std::allocator<void>>;

View File

@@ -74,14 +74,14 @@ public:
const rclcpp::QoS & qos = rclcpp::ClockQoS(),
bool use_clock_thread = true);
/// Attack node to the time source.
/// Attach node to the time source.
/**
* \param node std::shared pointer to a initialized node
*/
RCLCPP_PUBLIC
void attachNode(rclcpp::Node::SharedPtr node);
/// Attack node to the time source.
/// Attach node to the time source.
/**
* If the parameter `use_sim_time` is `true` then the source time is the simulation time,
* otherwise the source time is defined by the system.

View File

@@ -91,6 +91,17 @@ public:
void
reset();
/// Indicate that we're about to execute the callback.
/**
* The multithreaded executor takes advantage of this to avoid scheduling
* the callback multiple times.
*
* \return `true` if the callback should be executed, `false` if the timer was canceled.
*/
RCLCPP_PUBLIC
virtual bool
call() = 0;
/// Call the callback function when the timer signal is emitted.
RCLCPP_PUBLIC
virtual void
@@ -192,19 +203,28 @@ public:
}
/**
* \sa rclcpp::TimerBase::execute_callback
* \throws std::runtime_error if it failed to notify timer that callback occurred
* \sa rclcpp::TimerBase::call
* \throws std::runtime_error if it failed to notify timer that callback will occurr
*/
void
execute_callback() override
bool
call() override
{
rcl_ret_t ret = rcl_timer_call(timer_handle_.get());
if (ret == RCL_RET_TIMER_CANCELED) {
return;
return false;
}
if (ret != RCL_RET_OK) {
throw std::runtime_error("Failed to notify timer that callback occurred");
}
return true;
}
/**
* \sa rclcpp::TimerBase::execute_callback
*/
void
execute_callback() override
{
TRACEPOINT(callback_start, static_cast<const void *>(&callback_), false);
execute_callback_delegate<>();
TRACEPOINT(callback_end, static_cast<const void *>(&callback_));

View File

@@ -0,0 +1,201 @@
// Copyright 2021 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__TYPE_ADAPTER_HPP_
#define RCLCPP__TYPE_ADAPTER_HPP_
#include <type_traits>
namespace rclcpp
{
/// Template structure used to adapt custom, user-defined types to ROS types.
/**
* Adapting a custom, user-defined type to a ROS type allows that custom type
* to be used when publishing and subscribing in ROS.
*
* In order to adapt a custom type to a ROS type, the user must create a
* template specialization of this structure for the custom type.
* In that specialization they must:
*
* - change `is_specialized` to `std::true_type`,
* - specify the custom type with `using custom_type = ...`,
* - specify the ROS type with `using ros_message_type = ...`,
* - provide static convert functions with the signatures:
* - static void convert_to_ros(const custom_type &, ros_message_type &)
* - static void convert_to_custom(const ros_message_type &, custom_type &)
*
* The convert functions must convert from one type to the other.
*
* For example, here is a theoretical example for adapting `std::string` to the
* `std_msgs::msg::String` ROS message type:
*
* template<>
* struct rclcpp::TypeAdapter<std::string, std_msgs::msg::String>
* {
* using is_specialized = std::true_type;
* using custom_type = std::string;
* using ros_message_type = std_msgs::msg::String;
*
* static
* void
* convert_to_ros_message(
* const custom_type & source,
* ros_message_type & destination)
* {
* destination.data = source;
* }
*
* static
* void
* convert_to_custom(
* const ros_message_type & source,
* custom_type & destination)
* {
* destination = source.data;
* }
* };
*
* The adapter can then be used when creating a publisher or subscription,
* e.g.:
*
* using MyAdaptedType = TypeAdapter<std::string, std_msgs::msg::String>;
* auto pub = node->create_publisher<MyAdaptedType>("topic", 10);
* auto sub = node->create_subscription<MyAdaptedType>(
* "topic",
* 10,
* [](const std::string & msg) {...});
*
* You can also be more declarative by using the adapt_type::as metafunctions,
* which are a bit less ambiguous to read:
*
* using AdaptedType = rclcpp::adapt_type<std::string>::as<std_msgs::msg::String>;
* auto pub = node->create_publisher<AdaptedType>(...);
*
* If you wish, you may associate a custom type with a single ROS message type,
* allowing you to be a bit more brief when creating entities, e.g.:
*
* // First you must declare the association, this is similar to how you
* // would avoid using the namespace in C++ by doing `using std::vector;`.
* RCLCPP_USING_CUSTOM_TYPE_AS_ROS_MESSAGE_TYPE(std::string, std_msgs::msg::String);
*
* // Then you can create things with just the custom type, and the ROS
* // message type is implied based on the previous statement.
* auto pub = node->create_publisher<std::string>(...);
*/
template<typename CustomType, typename ROSMessageType = void, class Enable = void>
struct TypeAdapter
{
using is_specialized = std::false_type;
using custom_type = CustomType;
// In this case, the CustomType is the only thing given, or there is no specialization.
// Assign ros_message_type to CustomType for the former case.
using ros_message_type = CustomType;
};
/// Helper template to determine if a type is a TypeAdapter, false specialization.
template<typename T>
struct is_type_adapter : std::false_type {};
/// Helper template to determine if a type is a TypeAdapter, true specialization.
template<typename ... Ts>
struct is_type_adapter<TypeAdapter<Ts...>>: std::true_type {};
/// Identity specialization for TypeAdapter.
template<typename T>
struct TypeAdapter<T, void, std::enable_if_t<is_type_adapter<T>::value>>: T {};
namespace detail
{
template<typename CustomType, typename ROSMessageType>
struct assert_type_pair_is_specialized_type_adapter
{
using type_adapter = TypeAdapter<CustomType, ROSMessageType>;
static_assert(
type_adapter::is_specialized::value,
"No type adapter for this custom type/ros message type pair");
};
} // namespace detail
/// Template metafunction that can make the type being adapted explicit.
template<typename CustomType>
struct adapt_type
{
template<typename ROSMessageType>
using as = typename ::rclcpp::detail::assert_type_pair_is_specialized_type_adapter<
CustomType,
ROSMessageType
>::type_adapter;
};
/// Implicit type adapter used as a short hand way to create something with just the custom type.
/**
* This is used when creating a publisher or subscription using just the custom
* type in conjunction with RCLCPP_USING_CUSTOM_TYPE_AS_ROS_MESSAGE_TYPE().
* For example:
*
* #include "type_adapter_for_std_string_to_std_msgs_String.hpp"
*
* RCLCPP_USING_CUSTOM_TYPE_AS_ROS_MESSAGE_TYPE(std::string, std_msgs::msg::String);
*
* int main(...) {
* // ...
* auto pub = node->create_publisher<std::string>(...);
* }
*
* \sa TypeAdapter for more examples.
*/
template<typename CustomType>
struct ImplicitTypeAdapter
{
using is_specialized = std::false_type;
};
/// Specialization of TypeAdapter for ImplicitTypeAdapter.
/**
* This allows for things like this:
*
* RCLCPP_USING_CUSTOM_TYPE_AS_ROS_MESSAGE_TYPE(std::string, std_msgs::msg::String);
* auto pub = node->create_publisher<std::string>("topic", 10);
*
*/
template<typename T>
struct TypeAdapter<T, void, std::enable_if_t<ImplicitTypeAdapter<T>::is_specialized::value>>
: ImplicitTypeAdapter<T>
{};
/// Assigns the custom type implicitly to the given custom type/ros message type pair.
/**
* Note: this macro needs to be used in the root namespace.
* We cannot use ::rclcpp to protect against this, due to how GCC interprets the
* syntax, see: https://stackoverflow.com/a/2781537
*
* \sa TypeAdapter
* \sa ImplicitTypeAdapter
*/
#define RCLCPP_USING_CUSTOM_TYPE_AS_ROS_MESSAGE_TYPE(CustomType, ROSMessageType) \
template<> \
struct rclcpp::ImplicitTypeAdapter<CustomType> \
: public rclcpp::TypeAdapter<CustomType, ROSMessageType> \
{ \
static_assert( \
is_specialized::value, \
"Cannot use custom type as ros type when there is no TypeAdapter for that pair"); \
}
} // namespace rclcpp
#endif // RCLCPP__TYPE_ADAPTER_HPP_

View File

@@ -0,0 +1,100 @@
// Copyright 2021 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__WAIT_FOR_MESSAGE_HPP_
#define RCLCPP__WAIT_FOR_MESSAGE_HPP_
#include <memory>
#include <string>
#include "rclcpp/node.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rclcpp/wait_set.hpp"
namespace rclcpp
{
/// Wait for the next incoming message.
/**
* Given an already initialized subscription,
* wait for the next incoming message to arrive before the specified timeout.
*
* \param[out] out is the message to be filled when a new message is arriving.
* \param[in] subscription shared pointer to a previously initialized subscription.
* \param[in] context shared pointer to a context to watch for SIGINT requests.
* \param[in] time_to_wait parameter specifying the timeout before returning.
* \return true if a message was successfully received, false if message could not
* be obtained or shutdown was triggered asynchronously on the context.
*/
template<class MsgT, class Rep = int64_t, class Period = std::milli>
bool wait_for_message(
MsgT & out,
std::shared_ptr<rclcpp::Subscription<MsgT>> subscription,
std::shared_ptr<rclcpp::Context> context,
std::chrono::duration<Rep, Period> time_to_wait = std::chrono::duration<Rep, Period>(-1))
{
auto gc = std::make_shared<rclcpp::GuardCondition>(context);
auto shutdown_callback_handle = context->add_on_shutdown_callback(
[weak_gc = std::weak_ptr<rclcpp::GuardCondition>{gc}]() {
auto strong_gc = weak_gc.lock();
if (strong_gc) {
strong_gc->trigger();
}
});
rclcpp::WaitSet wait_set;
wait_set.add_subscription(subscription);
wait_set.add_guard_condition(gc);
auto ret = wait_set.wait(time_to_wait);
if (ret.kind() != rclcpp::WaitResultKind::Ready) {
return false;
}
if (wait_set.get_rcl_wait_set().guard_conditions[0]) {
return false;
}
rclcpp::MessageInfo info;
if (!subscription->take(out, info)) {
return false;
}
return true;
}
/// Wait for the next incoming message.
/**
* Wait for the next incoming message to arrive on a specified topic before the specified timeout.
*
* \param[out] out is the message to be filled when a new message is arriving.
* \param[in] node the node pointer to initialize the subscription on.
* \param[in] topic the topic to wait for messages.
* \param[in] time_to_wait parameter specifying the timeout before returning.
* \return true if a message was successfully received, false if message could not
* be obtained or shutdown was triggered asynchronously on the context.
*/
template<class MsgT, class Rep = int64_t, class Period = std::milli>
bool wait_for_message(
MsgT & out,
rclcpp::Node::SharedPtr node,
const std::string & topic,
std::chrono::duration<Rep, Period> time_to_wait = std::chrono::duration<Rep, Period>(-1))
{
auto sub = node->create_subscription<MsgT>(topic, 1, [](const std::shared_ptr<MsgT>) {});
return wait_for_message<MsgT, Rep, Period>(
out, sub, node->get_node_options().context(), time_to_wait);
}
} // namespace rclcpp
#endif // RCLCPP__WAIT_FOR_MESSAGE_HPP_

View File

@@ -20,13 +20,13 @@
#include <utility>
#include "rcl/wait.h"
#include "rcpputils/scope_exit.hpp"
#include "rclcpp/client.hpp"
#include "rclcpp/context.hpp"
#include "rclcpp/contexts/default_context.hpp"
#include "rclcpp/guard_condition.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/scope_exit.hpp"
#include "rclcpp/service.hpp"
#include "rclcpp/subscription_base.hpp"
#include "rclcpp/subscription_wait_set_mask.hpp"
@@ -657,7 +657,7 @@ public:
// ensure the ownership of the entities in the wait set is shared for the duration of wait
this->storage_acquire_ownerships();
RCLCPP_SCOPE_EXIT({this->storage_release_ownerships();});
RCPPUTILS_SCOPE_EXIT({this->storage_release_ownerships();});
// this method comes from the SynchronizationPolicy
return this->template sync_wait<WaitResult<WaitSetTemplate>>(

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>9.0.2</version>
<version>13.0.0</version>
<description>The ROS client library in C++.</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
<maintainer email="mabel@openrobotics.org">Mabel Zhang</maintainer>
@@ -11,6 +11,7 @@
<author email="dthomas@openrobotics.org">Dirk Thomas</author>
<buildtool_depend>ament_cmake_ros</buildtool_depend>
<buildtool_depend>python3</buildtool_depend>
<build_depend>ament_index_cpp</build_depend>
<build_depend>builtin_interfaces</build_depend>

View File

@@ -315,9 +315,13 @@ Context::shutdown(const std::string & reason)
// set shutdown reason
shutdown_reason_ = reason;
// call each shutdown callback
for (const auto & callback : on_shutdown_callbacks_) {
callback();
{
std::lock_guard<std::mutex> lock(on_shutdown_callbacks_mutex_);
for (const auto & callback : on_shutdown_callbacks_) {
(*callback)();
}
}
// interrupt all blocking sleep_for() and all blocking executors or wait sets
this->interrupt_all_sleep_for();
// remove self from the global contexts
@@ -344,20 +348,48 @@ Context::shutdown(const std::string & reason)
rclcpp::Context::OnShutdownCallback
Context::on_shutdown(OnShutdownCallback callback)
{
on_shutdown_callbacks_.push_back(callback);
add_on_shutdown_callback(callback);
return callback;
}
const std::vector<rclcpp::Context::OnShutdownCallback> &
Context::get_on_shutdown_callbacks() const
rclcpp::OnShutdownCallbackHandle
Context::add_on_shutdown_callback(OnShutdownCallback callback)
{
return on_shutdown_callbacks_;
auto callback_shared_ptr = std::make_shared<OnShutdownCallback>(callback);
{
std::lock_guard<std::mutex> lock(on_shutdown_callbacks_mutex_);
on_shutdown_callbacks_.emplace(callback_shared_ptr);
}
OnShutdownCallbackHandle callback_handle;
callback_handle.callback = callback_shared_ptr;
return callback_handle;
}
std::vector<rclcpp::Context::OnShutdownCallback> &
Context::get_on_shutdown_callbacks()
bool
Context::remove_on_shutdown_callback(const OnShutdownCallbackHandle & callback_handle)
{
return on_shutdown_callbacks_;
std::lock_guard<std::mutex> lock(on_shutdown_callbacks_mutex_);
auto callback_shared_ptr = callback_handle.callback.lock();
if (callback_shared_ptr == nullptr) {
return false;
}
return on_shutdown_callbacks_.erase(callback_shared_ptr) == 1;
}
std::vector<rclcpp::Context::OnShutdownCallback>
Context::get_on_shutdown_callbacks() const
{
std::vector<OnShutdownCallback> callbacks;
{
std::lock_guard<std::mutex> lock(on_shutdown_callbacks_mutex_);
for (auto & iter : on_shutdown_callbacks_) {
callbacks.emplace_back(*iter);
}
}
return callbacks;
}
std::shared_ptr<rcl_context_t>

View File

@@ -1,104 +0,0 @@
// Copyright 2021 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "rclcpp/detail/mutex_two_priorities.hpp"
#include <mutex>
namespace rclcpp
{
namespace detail
{
using LowPriorityLockable = MutexTwoPriorities::LowPriorityLockable;
using HighPriorityLockable = MutexTwoPriorities::HighPriorityLockable;
HighPriorityLockable::HighPriorityLockable(MutexTwoPriorities & parent)
: parent_(parent)
{}
void
HighPriorityLockable::lock()
{
std::unique_lock<std::mutex> guard{parent_.cv_mutex_};
if (parent_.data_taken_) {
++parent_.hp_waiting_count_;
while (parent_.data_taken_) {
parent_.hp_cv_.wait(guard);
}
--parent_.hp_waiting_count_;
}
parent_.data_taken_ = true;
}
void
HighPriorityLockable::unlock()
{
bool notify_lp{false};
{
std::lock_guard<std::mutex> guard{parent_.cv_mutex_};
parent_.data_taken_ = false;
notify_lp = 0u == parent_.hp_waiting_count_;
}
if (notify_lp) {
parent_.lp_cv_.notify_one();
} else {
parent_.hp_cv_.notify_one();
}
}
LowPriorityLockable::LowPriorityLockable(MutexTwoPriorities & parent)
: parent_(parent)
{}
void
LowPriorityLockable::lock()
{
std::unique_lock<std::mutex> guard{parent_.cv_mutex_};
while (parent_.data_taken_ || parent_.hp_waiting_count_) {
parent_.lp_cv_.wait(guard);
}
parent_.data_taken_ = true;
}
void
LowPriorityLockable::unlock()
{
bool notify_lp{false};
{
std::lock_guard<std::mutex> guard{parent_.cv_mutex_};
parent_.data_taken_ = false;
notify_lp = 0u == parent_.hp_waiting_count_;
}
if (notify_lp) {
parent_.lp_cv_.notify_one();
} else {
parent_.hp_cv_.notify_one();
}
}
HighPriorityLockable
MutexTwoPriorities::get_high_priority_lockable()
{
return HighPriorityLockable{*this};
}
LowPriorityLockable
MutexTwoPriorities::get_low_priority_lockable()
{
return LowPriorityLockable{*this};
}
} // namespace detail
} // namespace rclcpp

View File

@@ -19,8 +19,8 @@
#include <vector>
#include "rcl_yaml_param_parser/parser.h"
#include "rcpputils/scope_exit.hpp"
#include "rclcpp/scope_exit.hpp"
#include "rclcpp/parameter_map.hpp"
std::map<std::string, rclcpp::ParameterValue>
@@ -47,7 +47,7 @@ rclcpp::detail::resolve_parameter_overrides(
rclcpp::exceptions::throw_from_rcl_error(ret);
}
if (params) {
auto cleanup_params = make_scope_exit(
auto cleanup_params = rcpputils::make_scope_exit(
[params]() {
rcl_yaml_node_struct_fini(params);
});

View File

@@ -22,17 +22,19 @@
#include "rcl/allocator.h"
#include "rcl/error_handling.h"
#include "rcpputils/scope_exit.hpp"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/executor.hpp"
#include "rclcpp/guard_condition.hpp"
#include "rclcpp/memory_strategy.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/scope_exit.hpp"
#include "rclcpp/utilities.hpp"
#include "rcutils/logging_macros.h"
#include "tracetools/tracetools.h"
using namespace std::chrono_literals;
using rclcpp::exceptions::throw_from_rcl_error;
@@ -56,7 +58,7 @@ Executor::Executor(const rclcpp::ExecutorOptions & options)
throw_from_rcl_error(ret, "Failed to create interrupt guard condition in Executor constructor");
}
context_->on_shutdown(
shutdown_callback_handle_ = context_->add_on_shutdown_callback(
[weak_gc = std::weak_ptr<rclcpp::GuardCondition>{shutdown_guard_condition_}]() {
auto strong_gc = weak_gc.lock();
if (strong_gc) {
@@ -138,6 +140,14 @@ Executor::~Executor()
}
// Remove and release the sigint guard condition
memory_strategy_->remove_guard_condition(&shutdown_guard_condition_->get_rcl_guard_condition());
// Remove shutdown callback handle registered to Context
if (!context_->remove_on_shutdown_callback(shutdown_callback_handle_)) {
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"failed to remove registered on_shutdown callback");
rcl_reset_error();
}
}
std::vector<rclcpp::CallbackGroup::WeakPtr>
@@ -182,14 +192,12 @@ Executor::add_callback_groups_from_nodes_associated_to_executor()
for (auto & weak_node : weak_nodes_) {
auto node = weak_node.lock();
if (node) {
auto group_ptrs = node->get_callback_groups();
std::for_each(
group_ptrs.begin(), group_ptrs.end(),
[this, node](rclcpp::CallbackGroup::WeakPtr group_ptr)
node->for_each_callback_group(
[this, node](rclcpp::CallbackGroup::SharedPtr shared_group_ptr)
{
auto shared_group_ptr = group_ptr.lock();
if (shared_group_ptr && shared_group_ptr->automatically_add_to_executor_with_node() &&
!shared_group_ptr->get_associated_with_executor_atomic().load())
if (
shared_group_ptr->automatically_add_to_executor_with_node() &&
!shared_group_ptr->get_associated_with_executor_atomic().load())
{
this->add_callback_group_to_map(
shared_group_ptr,
@@ -260,21 +268,25 @@ Executor::add_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_pt
// If the node already has an executor
std::atomic_bool & has_executor = node_ptr->get_associated_with_executor_atomic();
if (has_executor.exchange(true)) {
throw std::runtime_error("Node has already been added to an executor.");
throw std::runtime_error(
std::string("Node '") + node_ptr->get_fully_qualified_name() +
"' has already been added to an executor.");
}
std::lock_guard<std::mutex> guard{mutex_};
for (auto & weak_group : node_ptr->get_callback_groups()) {
auto group_ptr = weak_group.lock();
if (group_ptr != nullptr && !group_ptr->get_associated_with_executor_atomic().load() &&
group_ptr->automatically_add_to_executor_with_node())
node_ptr->for_each_callback_group(
[this, node_ptr, notify](rclcpp::CallbackGroup::SharedPtr group_ptr)
{
this->add_callback_group_to_map(
group_ptr,
node_ptr,
weak_groups_to_nodes_associated_with_executor_,
notify);
}
}
if (!group_ptr->get_associated_with_executor_atomic().load() &&
group_ptr->automatically_add_to_executor_with_node())
{
this->add_callback_group_to_map(
group_ptr,
node_ptr,
weak_groups_to_nodes_associated_with_executor_,
notify);
}
});
weak_nodes_.push_back(node_ptr);
}
@@ -440,7 +452,7 @@ Executor::spin_some_impl(std::chrono::nanoseconds max_duration, bool exhaustive)
if (spinning.exchange(true)) {
throw std::runtime_error("spin_some() called while already spinning");
}
RCLCPP_SCOPE_EXIT(this->spinning.store(false); );
RCPPUTILS_SCOPE_EXIT(this->spinning.store(false); );
bool work_available = false;
while (rclcpp::ok(context_) && spinning.load() && max_duration_not_elapsed()) {
AnyExecutable any_exec;
@@ -474,7 +486,7 @@ Executor::spin_once(std::chrono::nanoseconds timeout)
if (spinning.exchange(true)) {
throw std::runtime_error("spin_once() called while already spinning");
}
RCLCPP_SCOPE_EXIT(this->spinning.store(false); );
RCPPUTILS_SCOPE_EXIT(this->spinning.store(false); );
spin_once_impl(timeout);
}
@@ -505,9 +517,15 @@ Executor::execute_any_executable(AnyExecutable & any_exec)
return;
}
if (any_exec.timer) {
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(
rclcpp_executor_execute,
static_cast<const void *>(any_exec.subscription->get_subscription_handle().get()));
execute_subscription(any_exec.subscription);
}
if (any_exec.service) {
@@ -581,8 +599,7 @@ Executor::execute_subscription(rclcpp::SubscriptionBase::SharedPtr subscription)
[&]() {return subscription->take_serialized(*serialized_msg.get(), message_info);},
[&]()
{
auto void_serialized_msg = std::static_pointer_cast<void>(serialized_msg);
subscription->handle_message(void_serialized_msg, message_info);
subscription->handle_serialized_message(serialized_msg, message_info);
});
subscription->return_serialized_message(serialized_msg);
} else if (subscription->can_loan_messages()) {
@@ -669,6 +686,7 @@ Executor::execute_client(
void
Executor::wait_for_work(std::chrono::nanoseconds timeout)
{
TRACEPOINT(rclcpp_executor_wait_for_work, timeout.count());
{
std::lock_guard<std::mutex> guard(mutex_);
@@ -818,6 +836,7 @@ Executor::get_next_ready_executable_from_map(
const rclcpp::memory_strategy::MemoryStrategy::WeakCallbackGroupsToNodesMap &
weak_groups_to_nodes)
{
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

@@ -19,10 +19,10 @@
#include <memory>
#include <vector>
#include "rclcpp/utilities.hpp"
#include "rclcpp/scope_exit.hpp"
#include "rcpputils/scope_exit.hpp"
#include "rclcpp/utilities.hpp"
using rclcpp::detail::MutexTwoPriorities;
using rclcpp::executors::MultiThreadedExecutor;
MultiThreadedExecutor::MultiThreadedExecutor(
@@ -48,12 +48,11 @@ MultiThreadedExecutor::spin()
if (spinning.exchange(true)) {
throw std::runtime_error("spin() called while already spinning");
}
RCLCPP_SCOPE_EXIT(this->spinning.store(false); );
RCPPUTILS_SCOPE_EXIT(this->spinning.store(false); );
std::vector<std::thread> threads;
size_t thread_id = 0;
{
auto low_priority_wait_mutex = wait_mutex_.get_low_priority_lockable();
std::lock_guard<MutexTwoPriorities::LowPriorityLockable> wait_lock(low_priority_wait_mutex);
std::lock_guard wait_lock{wait_mutex_};
for (; thread_id < number_of_threads_ - 1; ++thread_id) {
auto func = std::bind(&MultiThreadedExecutor::run, this, thread_id);
threads.emplace_back(func);
@@ -78,26 +77,13 @@ MultiThreadedExecutor::run(size_t)
while (rclcpp::ok(this->context_) && spinning.load()) {
rclcpp::AnyExecutable any_exec;
{
auto low_priority_wait_mutex = wait_mutex_.get_low_priority_lockable();
std::lock_guard<MutexTwoPriorities::LowPriorityLockable> wait_lock(low_priority_wait_mutex);
std::lock_guard wait_lock{wait_mutex_};
if (!rclcpp::ok(this->context_) || !spinning.load()) {
return;
}
if (!get_next_executable(any_exec, next_exec_timeout_)) {
continue;
}
if (any_exec.timer) {
// Guard against multiple threads getting the same timer.
if (scheduled_timers_.count(any_exec.timer) != 0) {
// Make sure that any_exec's callback group is reset before
// the lock is released.
if (any_exec.callback_group) {
any_exec.callback_group->can_be_taken_from().store(true);
}
continue;
}
scheduled_timers_.insert(any_exec.timer);
}
}
if (yield_before_execute_) {
std::this_thread::yield();
@@ -105,14 +91,6 @@ MultiThreadedExecutor::run(size_t)
execute_any_executable(any_exec);
if (any_exec.timer) {
auto high_priority_wait_mutex = wait_mutex_.get_high_priority_lockable();
std::lock_guard<MutexTwoPriorities::HighPriorityLockable> wait_lock(high_priority_wait_mutex);
auto it = scheduled_timers_.find(any_exec.timer);
if (it != scheduled_timers_.end()) {
scheduled_timers_.erase(it);
}
}
// Clear the callback_group to prevent the AnyExecutable destructor from
// resetting the callback group `can_be_taken_from`
any_exec.callback_group.reset();

View File

@@ -12,9 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "rcpputils/scope_exit.hpp"
#include "rclcpp/executors/single_threaded_executor.hpp"
#include "rclcpp/any_executable.hpp"
#include "rclcpp/scope_exit.hpp"
using rclcpp::executors::SingleThreadedExecutor;
@@ -29,7 +30,7 @@ SingleThreadedExecutor::spin()
if (spinning.exchange(true)) {
throw std::runtime_error("spin() called while already spinning");
}
RCLCPP_SCOPE_EXIT(this->spinning.store(false); );
RCPPUTILS_SCOPE_EXIT(this->spinning.store(false); );
while (rclcpp::ok(this->context_) && spinning.load()) {
rclcpp::AnyExecutable any_executable;
if (get_next_executable(any_executable)) {

View File

@@ -61,8 +61,7 @@ StaticExecutorEntitiesCollector::~StaticExecutorEntitiesCollector()
void
StaticExecutorEntitiesCollector::init(
rcl_wait_set_t * p_wait_set,
rclcpp::memory_strategy::MemoryStrategy::SharedPtr memory_strategy,
rcl_guard_condition_t * executor_guard_condition)
rclcpp::memory_strategy::MemoryStrategy::SharedPtr memory_strategy)
{
// Empty initialize executable list
exec_list_ = rclcpp::experimental::ExecutableList();
@@ -74,9 +73,6 @@ StaticExecutorEntitiesCollector::init(
}
memory_strategy_ = memory_strategy;
// Add executor's guard condition
memory_strategy_->add_guard_condition(executor_guard_condition);
// Get memory strategy and executable list. Prepare wait_set_
std::shared_ptr<void> shared_ptr;
execute(shared_ptr);
@@ -108,6 +104,14 @@ StaticExecutorEntitiesCollector::execute(std::shared_ptr<void> & data)
fill_executable_list();
// Resize the wait_set_ based on memory_strategy handles (rcl_wait_set_resize)
prepare_wait_set();
// Add new nodes guard conditions to map
std::lock_guard<std::mutex> guard{new_nodes_mutex_};
for (const auto & weak_node : new_nodes_) {
if (auto node_ptr = weak_node.lock()) {
weak_nodes_to_guard_conditions_[node_ptr] = node_ptr->get_notify_guard_condition();
}
}
new_nodes_.clear();
}
void
@@ -277,7 +281,8 @@ StaticExecutorEntitiesCollector::add_to_wait_set(rcl_wait_set_t * wait_set)
size_t StaticExecutorEntitiesCollector::get_number_of_ready_guard_conditions()
{
return weak_nodes_to_guard_conditions_.size();
std::lock_guard<std::mutex> guard{new_nodes_mutex_};
return weak_nodes_to_guard_conditions_.size() + new_nodes_.size();
}
bool
@@ -290,18 +295,20 @@ StaticExecutorEntitiesCollector::add_node(
if (has_executor.exchange(true)) {
throw std::runtime_error("Node has already been added to an executor.");
}
for (const auto & weak_group : node_ptr->get_callback_groups()) {
auto group_ptr = weak_group.lock();
if (group_ptr != nullptr && !group_ptr->get_associated_with_executor_atomic().load() &&
group_ptr->automatically_add_to_executor_with_node())
node_ptr->for_each_callback_group(
[this, node_ptr, &is_new_node](rclcpp::CallbackGroup::SharedPtr group_ptr)
{
is_new_node = (add_callback_group(
if (
!group_ptr->get_associated_with_executor_atomic().load() &&
group_ptr->automatically_add_to_executor_with_node())
{
is_new_node = (add_callback_group(
group_ptr,
node_ptr,
weak_groups_to_nodes_associated_with_executor_) ||
is_new_node);
}
}
}
});
weak_nodes_.push_back(node_ptr);
return is_new_node;
}
@@ -327,7 +334,8 @@ StaticExecutorEntitiesCollector::add_callback_group(
throw std::runtime_error("Callback group was already added to executor.");
}
if (is_new_node) {
weak_nodes_to_guard_conditions_[node_ptr] = node_ptr->get_notify_guard_condition();
std::lock_guard<std::mutex> guard{new_nodes_mutex_};
new_nodes_.push_back(node_ptr);
return true;
}
return false;
@@ -467,13 +475,10 @@ StaticExecutorEntitiesCollector::add_callback_groups_from_nodes_associated_to_ex
for (const auto & weak_node : weak_nodes_) {
auto node = weak_node.lock();
if (node) {
auto group_ptrs = node->get_callback_groups();
std::for_each(
group_ptrs.begin(), group_ptrs.end(),
[this, node](rclcpp::CallbackGroup::WeakPtr group_ptr)
node->for_each_callback_group(
[this, node](rclcpp::CallbackGroup::SharedPtr shared_group_ptr)
{
auto shared_group_ptr = group_ptr.lock();
if (shared_group_ptr && shared_group_ptr->automatically_add_to_executor_with_node() &&
if (shared_group_ptr->automatically_add_to_executor_with_node() &&
!shared_group_ptr->get_associated_with_executor_atomic().load())
{
add_callback_group(

View File

@@ -16,9 +16,10 @@
#include <chrono>
#include <memory>
#include <utility>
#include <vector>
#include "rclcpp/scope_exit.hpp"
#include "rcpputils/scope_exit.hpp"
using rclcpp::executors::StaticSingleThreadedExecutor;
using rclcpp::experimental::ExecutableList;
@@ -43,11 +44,11 @@ StaticSingleThreadedExecutor::spin()
if (spinning.exchange(true)) {
throw std::runtime_error("spin() called while already spinning");
}
RCLCPP_SCOPE_EXIT(this->spinning.store(false); );
RCPPUTILS_SCOPE_EXIT(this->spinning.store(false); );
// Set memory_strategy_ and exec_list_ based on weak_nodes_
// Prepare wait_set_ based on memory_strategy_
entities_collector_->init(&wait_set_, memory_strategy_, &interrupt_guard_condition_);
entities_collector_->init(&wait_set_, memory_strategy_);
while (rclcpp::ok(this->context_) && spinning.load()) {
// Refresh wait set and wait for work
@@ -81,7 +82,7 @@ StaticSingleThreadedExecutor::spin_some_impl(std::chrono::nanoseconds max_durati
{
// Make sure the entities collector has been initialized
if (!entities_collector_->is_init()) {
entities_collector_->init(&wait_set_, memory_strategy_, &interrupt_guard_condition_);
entities_collector_->init(&wait_set_, memory_strategy_);
}
auto start = std::chrono::steady_clock::now();
@@ -100,7 +101,7 @@ StaticSingleThreadedExecutor::spin_some_impl(std::chrono::nanoseconds max_durati
if (spinning.exchange(true)) {
throw std::runtime_error("spin_some() called while already spinning");
}
RCLCPP_SCOPE_EXIT(this->spinning.store(false); );
RCPPUTILS_SCOPE_EXIT(this->spinning.store(false); );
while (rclcpp::ok(context_) && spinning.load() && max_duration_not_elapsed()) {
// Get executables that are ready now
@@ -118,7 +119,7 @@ StaticSingleThreadedExecutor::spin_once_impl(std::chrono::nanoseconds timeout)
{
// Make sure the entities collector has been initialized
if (!entities_collector_->is_init()) {
entities_collector_->init(&wait_set_, memory_strategy_, &interrupt_guard_condition_);
entities_collector_->init(&wait_set_, memory_strategy_);
}
if (rclcpp::ok(context_) && spinning.load()) {
@@ -237,7 +238,9 @@ StaticSingleThreadedExecutor::execute_ready_executables(bool spin_once)
for (size_t i = 0; i < wait_set_.size_of_timers; ++i) {
if (i < entities_collector_->get_number_of_timers()) {
if (wait_set_.timers[i] && entities_collector_->get_timer(i)->is_ready()) {
execute_timer(entities_collector_->get_timer(i));
auto timer = entities_collector_->get_timer(i);
timer->call();
execute_timer(std::move(timer));
if (spin_once) {
return true;
}

View File

@@ -19,7 +19,6 @@
#include "rcl/expand_topic_name.h"
#include "rcl/validate_topic_name.h"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/scope_exit.hpp"
#include "rcutils/logging_macros.h"
#include "rcutils/types/string_map.h"
#include "rmw/error_handling.h"

View File

@@ -36,11 +36,19 @@ std::shared_ptr<rclcpp::SerializedMessage> GenericSubscription::create_serialize
}
void GenericSubscription::handle_message(
std::shared_ptr<void> & message, const rclcpp::MessageInfo & message_info)
std::shared_ptr<void> &,
const rclcpp::MessageInfo &)
{
(void) message_info;
auto typed_message = std::static_pointer_cast<rclcpp::SerializedMessage>(message);
callback_(typed_message);
throw rclcpp::exceptions::UnimplementedError(
"handle_message is not implemented for GenericSubscription");
}
void
GenericSubscription::handle_serialized_message(
const std::shared_ptr<rclcpp::SerializedMessage> & message,
const rclcpp::MessageInfo &)
{
callback_(message);
}
void GenericSubscription::handle_loaned_message(

View File

@@ -36,23 +36,26 @@ IntraProcessManager::add_publisher(rclcpp::PublisherBase::SharedPtr publisher)
{
std::unique_lock<std::shared_timed_mutex> lock(mutex_);
auto id = IntraProcessManager::get_next_unique_id();
uint64_t pub_id = IntraProcessManager::get_next_unique_id();
publishers_[id].publisher = publisher;
publishers_[id].topic_name = publisher->get_topic_name();
publishers_[id].qos = publisher->get_actual_qos().get_rmw_qos_profile();
publishers_[pub_id] = publisher;
// Initialize the subscriptions storage for this publisher.
pub_to_subs_[id] = SplittedSubscriptions();
pub_to_subs_[pub_id] = SplittedSubscriptions();
// create an entry for the publisher id and populate with already existing subscriptions
for (auto & pair : subscriptions_) {
if (can_communicate(publishers_[id], pair.second)) {
insert_sub_id_for_pub(pair.first, id, pair.second.use_take_shared_method);
auto subscription = pair.second.lock();
if (!subscription) {
continue;
}
if (can_communicate(publisher, subscription)) {
uint64_t sub_id = pair.first;
insert_sub_id_for_pub(sub_id, pub_id, subscription->use_take_shared_method());
}
}
return id;
return pub_id;
}
uint64_t
@@ -60,21 +63,23 @@ IntraProcessManager::add_subscription(SubscriptionIntraProcessBase::SharedPtr su
{
std::unique_lock<std::shared_timed_mutex> lock(mutex_);
auto id = IntraProcessManager::get_next_unique_id();
uint64_t sub_id = IntraProcessManager::get_next_unique_id();
subscriptions_[id].subscription = subscription;
subscriptions_[id].topic_name = subscription->get_topic_name();
subscriptions_[id].qos = subscription->get_actual_qos();
subscriptions_[id].use_take_shared_method = subscription->use_take_shared_method();
subscriptions_[sub_id] = subscription;
// adds the subscription id to all the matchable publishers
for (auto & pair : publishers_) {
if (can_communicate(pair.second, subscriptions_[id])) {
insert_sub_id_for_pub(id, pair.first, subscriptions_[id].use_take_shared_method);
auto publisher = pair.second.lock();
if (!publisher) {
continue;
}
if (can_communicate(publisher, subscription)) {
uint64_t pub_id = pair.first;
insert_sub_id_for_pub(sub_id, pub_id, subscription->use_take_shared_method());
}
}
return id;
return sub_id;
}
void
@@ -116,7 +121,7 @@ IntraProcessManager::matches_any_publishers(const rmw_gid_t * id) const
std::shared_lock<std::shared_timed_mutex> lock(mutex_);
for (auto & publisher_pair : publishers_) {
auto publisher = publisher_pair.second.publisher.lock();
auto publisher = publisher_pair.second.lock();
if (!publisher) {
continue;
}
@@ -157,7 +162,7 @@ IntraProcessManager::get_subscription_intra_process(uint64_t intra_process_subsc
if (subscription_it == subscriptions_.end()) {
return nullptr;
} else {
auto subscription = subscription_it->second.subscription.lock();
auto subscription = subscription_it->second.lock();
if (subscription) {
return subscription;
} else {
@@ -204,25 +209,16 @@ IntraProcessManager::insert_sub_id_for_pub(
bool
IntraProcessManager::can_communicate(
PublisherInfo pub_info,
SubscriptionInfo sub_info) const
rclcpp::PublisherBase::SharedPtr pub,
rclcpp::experimental::SubscriptionIntraProcessBase::SharedPtr sub) const
{
// publisher and subscription must be on the same topic
if (strcmp(pub_info.topic_name, sub_info.topic_name) != 0) {
if (strcmp(pub->get_topic_name(), sub->get_topic_name()) != 0) {
return false;
}
// TODO(alsora): the following checks for qos compatibility should be provided by the RMW
// a reliable subscription can't be connected with a best effort publisher
if (
sub_info.qos.reliability == RMW_QOS_POLICY_RELIABILITY_RELIABLE &&
pub_info.qos.reliability == RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT)
{
return false;
}
// a publisher and a subscription with different durability can't communicate
if (sub_info.qos.durability != pub_info.qos.durability) {
auto check_result = rclcpp::qos_check_compatible(pub->get_actual_qos(), sub->get_actual_qos());
if (check_result.compatibility == rclcpp::QoSCompatibility::Error) {
return false;
}

View File

@@ -61,6 +61,12 @@ extend_sub_namespace(const std::string & existing_sub_namespace, const std::stri
extension.c_str(),
"a sub-namespace should not have a leading /",
0);
} else if (existing_sub_namespace.empty() && extension.empty()) {
throw rclcpp::exceptions::NameValidationError(
"sub_namespace",
extension.c_str(),
"sub-nodes should not extend nodes by an empty sub-namespace",
0);
}
std::string new_sub_namespace;
@@ -86,7 +92,11 @@ create_effective_namespace(const std::string & node_namespace, const std::string
// and do not need trimming of `/` and other things, as they were validated
// in other functions already.
if (node_namespace.back() == '/') {
// A node may not have a sub_namespace if it is no sub_node. In this case,
// just return the original namespace
if (sub_namespace.empty()) {
return node_namespace;
} else if (node_namespace.back() == '/') {
// this is the special case where node_namespace is just `/`
return node_namespace + sub_namespace;
} else {
@@ -482,10 +492,11 @@ Node::get_subscriptions_info_by_topic(const std::string & topic_name, bool no_ma
return node_graph_->get_subscriptions_info_by_topic(topic_name, no_mangle);
}
const std::vector<rclcpp::CallbackGroup::WeakPtr> &
Node::get_callback_groups() const
void
Node::for_each_callback_group(
const node_interfaces::NodeBaseInterface::CallbackGroupFunction & func)
{
return node_base_->get_callback_groups();
node_base_->for_each_callback_group(func);
}
rclcpp::Event::SharedPtr

View File

@@ -221,12 +221,10 @@ NodeBase::create_callback_group(
rclcpp::CallbackGroupType group_type,
bool automatically_add_to_executor_with_node)
{
using rclcpp::CallbackGroup;
using rclcpp::CallbackGroupType;
auto group = CallbackGroup::SharedPtr(
new CallbackGroup(
group_type,
automatically_add_to_executor_with_node));
auto group = std::make_shared<rclcpp::CallbackGroup>(
group_type,
automatically_add_to_executor_with_node);
std::lock_guard<std::mutex> lock(callback_groups_mutex_);
callback_groups_.push_back(group);
return group;
}
@@ -240,20 +238,25 @@ NodeBase::get_default_callback_group()
bool
NodeBase::callback_group_in_node(rclcpp::CallbackGroup::SharedPtr group)
{
bool group_belongs_to_this_node = false;
std::lock_guard<std::mutex> lock(callback_groups_mutex_);
for (auto & weak_group : this->callback_groups_) {
auto cur_group = weak_group.lock();
if (cur_group && (cur_group == group)) {
group_belongs_to_this_node = true;
return true;
}
}
return group_belongs_to_this_node;
return false;
}
const std::vector<rclcpp::CallbackGroup::WeakPtr> &
NodeBase::get_callback_groups() const
void NodeBase::for_each_callback_group(const CallbackGroupFunction & func)
{
return callback_groups_;
std::lock_guard<std::mutex> lock(callback_groups_mutex_);
for (rclcpp::CallbackGroup::WeakPtr & weak_group : this->callback_groups_) {
rclcpp::CallbackGroup::SharedPtr group = weak_group.lock();
if (group) {
func(group);
}
}
}
std::atomic_bool &

View File

@@ -32,7 +32,6 @@
#include "rcl_interfaces/srv/list_parameters.hpp"
#include "rclcpp/create_publisher.hpp"
#include "rclcpp/parameter_map.hpp"
#include "rclcpp/scope_exit.hpp"
#include "rcutils/logging_macros.h"
#include "rmw/qos_profiles.h"
@@ -349,6 +348,21 @@ __declare_parameter_common(
initial_value = &overrides_it->second;
}
// If there is no initial value, then skip initialization
if (initial_value->get_type() == rclcpp::PARAMETER_NOT_SET) {
// Add declared parameters to storage (without a value)
parameter_infos[name].descriptor.name = name;
if (parameter_descriptor.dynamic_typing) {
parameter_infos[name].descriptor.type = rclcpp::PARAMETER_NOT_SET;
} else {
parameter_infos[name].descriptor.type = parameter_descriptor.type;
}
parameters_out[name] = parameter_infos.at(name);
rcl_interfaces::msg::SetParametersResult result;
result.successful = true;
return result;
}
// Check with the user's callback to see if the initial value can be set.
std::vector<rclcpp::Parameter> parameter_wrappers {rclcpp::Parameter(name, *initial_value)};
// This function also takes care of default vs initial value.
@@ -413,14 +427,6 @@ declare_parameter_helper(
parameter_descriptor.type = static_cast<uint8_t>(type);
}
if (
rclcpp::PARAMETER_NOT_SET == default_value.get_type() &&
overrides.find(name) == overrides.end() &&
parameter_descriptor.dynamic_typing == false)
{
throw rclcpp::exceptions::NoParameterOverrideProvided(name);
}
rcl_interfaces::msg::ParameterEvent parameter_event;
auto result = __declare_parameter_common(
name,
@@ -806,14 +812,21 @@ NodeParameters::get_parameters(const std::vector<std::string> & names) const
rclcpp::Parameter
NodeParameters::get_parameter(const std::string & name) const
{
rclcpp::Parameter parameter;
std::lock_guard<std::recursive_mutex> lock(mutex_);
if (get_parameter(name, parameter)) {
return parameter;
auto param_iter = parameters_.find(name);
if (
parameters_.end() != param_iter &&
(param_iter->second.value.get_type() != rclcpp::ParameterType::PARAMETER_NOT_SET ||
param_iter->second.descriptor.dynamic_typing))
{
return rclcpp::Parameter{name, param_iter->second.value};
} else if (this->allow_undeclared_) {
return parameter;
} else {
return rclcpp::Parameter{};
} else if (parameters_.end() == param_iter) {
throw rclcpp::exceptions::ParameterNotDeclaredException(name);
} else {
throw rclcpp::exceptions::ParameterUninitializedException(name);
}
}

View File

@@ -143,6 +143,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));
if (RCL_RET_SUBSCRIPTION_TAKE_FAILED == ret) {
return false;
} else if (RCL_RET_OK != ret) {

View File

@@ -31,7 +31,7 @@ SubscriptionIntraProcessBase::get_topic_name() const
return topic_name_.c_str();
}
rmw_qos_profile_t
rclcpp::QoS
SubscriptionIntraProcessBase::get_actual_qos() const
{
return qos_profile_;

View File

@@ -156,12 +156,6 @@ ok(Context::SharedPtr context)
return context->is_valid();
}
bool
is_initialized(Context::SharedPtr context)
{
return ok(context);
}
bool
shutdown(Context::SharedPtr context, const std::string & reason)
{

View File

@@ -20,7 +20,7 @@
#include "performance_test_fixture/performance_test_fixture.hpp"
#include "rclcpp/rclcpp.hpp"
#include "rclcpp/scope_exit.hpp"
#include "rcpputils/scope_exit.hpp"
#include "test_msgs/msg/empty.hpp"
using namespace std::chrono_literals;
@@ -366,7 +366,7 @@ BENCHMARK_F(
if (ret != RCL_RET_OK) {
st.SkipWithError(rcutils_get_error_string().str);
}
RCLCPP_SCOPE_EXIT(
RCPPUTILS_SCOPE_EXIT(
{
rcl_ret_t ret = rcl_wait_set_fini(&wait_set);
if (ret != RCL_RET_OK) {
@@ -376,10 +376,9 @@ BENCHMARK_F(
auto memory_strategy = rclcpp::memory_strategies::create_default_strategy();
rclcpp::GuardCondition guard_condition(shared_context);
rcl_guard_condition_t rcl_guard_condition = guard_condition.get_rcl_guard_condition();
entities_collector_->init(&wait_set, memory_strategy, &rcl_guard_condition);
RCLCPP_SCOPE_EXIT(entities_collector_->fini());
entities_collector_->init(&wait_set, memory_strategy);
RCPPUTILS_SCOPE_EXIT(entities_collector_->fini());
reset_heap_counters();

View File

@@ -0,0 +1 @@
string data

View File

@@ -7,10 +7,13 @@ add_definitions(-DTEST_RESOURCES_DIRECTORY="${TEST_RESOURCES_DIRECTORY}")
rosidl_generate_interfaces(${PROJECT_NAME}_test_msgs
../msg/Header.msg
../msg/MessageWithHeader.msg
../msg/String.msg
DEPENDENCIES builtin_interfaces
LIBRARY_NAME ${PROJECT_NAME}
SKIP_INSTALL
)
# Need the target name to depend on generated interface libraries
rosidl_get_typesupport_target(cpp_typesupport_target "${PROJECT_NAME}_test_msgs" "rosidl_typesupport_cpp")
ament_add_gtest(
test_allocator_common
@@ -143,6 +146,13 @@ if(TARGET test_intra_process_manager)
)
target_link_libraries(test_intra_process_manager ${PROJECT_NAME})
endif()
ament_add_gmock(test_intra_process_manager_with_allocators test_intra_process_manager_with_allocators.cpp)
if(TARGET test_intra_process_manager_with_allocators)
ament_target_dependencies(test_intra_process_manager_with_allocators
"test_msgs"
)
target_link_libraries(test_intra_process_manager_with_allocators ${PROJECT_NAME})
endif()
ament_add_gtest(test_ring_buffer_implementation test_ring_buffer_implementation.cpp)
if(TARGET test_ring_buffer_implementation)
ament_target_dependencies(test_ring_buffer_implementation
@@ -348,6 +358,48 @@ if(TARGET test_publisher)
)
target_link_libraries(test_publisher ${PROJECT_NAME} mimick)
endif()
set(append_library_dirs "${CMAKE_CURRENT_BINARY_DIR}")
if(WIN32)
set(append_library_dirs "${append_library_dirs}/$<CONFIG>")
endif()
ament_add_gtest(test_publisher_with_type_adapter test_publisher_with_type_adapter.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}"
)
if(TARGET test_publisher_with_type_adapter)
ament_target_dependencies(test_publisher_with_type_adapter
"rcutils"
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
"test_msgs"
)
target_link_libraries(test_publisher_with_type_adapter
${PROJECT_NAME}
mimick
${cpp_typesupport_target})
endif()
ament_add_gtest(test_subscription_with_type_adapter test_subscription_with_type_adapter.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}"
)
if(TARGET test_subscription_with_type_adapter)
ament_target_dependencies(test_subscription_with_type_adapter
"rcutils"
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
"test_msgs"
)
target_link_libraries(test_subscription_with_type_adapter
${PROJECT_NAME}
mimick
${cpp_typesupport_target})
endif()
ament_add_gtest(test_publisher_subscription_count_api test_publisher_subscription_count_api.cpp)
if(TARGET test_publisher_subscription_count_api)
ament_target_dependencies(test_publisher_subscription_count_api
@@ -500,11 +552,6 @@ if(TARGET test_find_weak_nodes)
target_link_libraries(test_find_weak_nodes ${PROJECT_NAME})
endif()
set(append_library_dirs "${CMAKE_CURRENT_BINARY_DIR}")
if(WIN32)
set(append_library_dirs "${append_library_dirs}/$<CONFIG>")
endif()
ament_add_gtest(test_externally_defined_services test_externally_defined_services.cpp)
ament_target_dependencies(test_externally_defined_services
"rcl"
@@ -558,6 +605,13 @@ if(TARGET test_utilities)
target_link_libraries(test_utilities ${PROJECT_NAME} mimick)
endif()
ament_add_gtest(test_wait_for_message test_wait_for_message.cpp)
if(TARGET test_wait_for_message)
ament_target_dependencies(test_wait_for_message
"test_msgs")
target_link_libraries(test_wait_for_message ${PROJECT_NAME})
endif()
ament_add_gtest(test_interface_traits test_interface_traits.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_interface_traits)
@@ -632,8 +686,9 @@ if(TARGET test_subscription_topic_statistics)
"rosidl_typesupport_cpp"
"statistics_msgs"
"test_msgs")
rosidl_target_interfaces(test_subscription_topic_statistics ${PROJECT_NAME}_test_msgs "rosidl_typesupport_cpp")
target_link_libraries(test_subscription_topic_statistics ${PROJECT_NAME})
target_link_libraries(test_subscription_topic_statistics
${PROJECT_NAME}
${cpp_typesupport_target})
endif()
ament_add_gtest(test_subscription_options test_subscription_options.cpp)

View File

@@ -20,6 +20,8 @@
#include "rclcpp/rclcpp.hpp"
#include "rcpputils/scope_exit.hpp"
#include "test_msgs/msg/empty.hpp"
#include "test_msgs/srv/empty.hpp"
@@ -41,38 +43,38 @@ struct NumberOfEntities
std::unique_ptr<NumberOfEntities> get_number_of_default_entities(rclcpp::Node::SharedPtr node)
{
auto number_of_entities = std::make_unique<NumberOfEntities>();
for (auto & weak_group : node->get_callback_groups()) {
auto group = weak_group.lock();
EXPECT_NE(nullptr, group);
if (!group || !group->can_be_taken_from().load()) {
return nullptr;
}
group->find_subscription_ptrs_if(
[&number_of_entities](rclcpp::SubscriptionBase::SharedPtr &)
{
number_of_entities->subscriptions++; return false;
});
group->find_timer_ptrs_if(
[&number_of_entities](rclcpp::TimerBase::SharedPtr &)
{
number_of_entities->timers++; return false;
});
group->find_service_ptrs_if(
[&number_of_entities](rclcpp::ServiceBase::SharedPtr &)
{
number_of_entities->services++; return false;
});
group->find_client_ptrs_if(
[&number_of_entities](rclcpp::ClientBase::SharedPtr &)
{
number_of_entities->clients++; return false;
});
group->find_waitable_ptrs_if(
[&number_of_entities](rclcpp::Waitable::SharedPtr &)
{
number_of_entities->waitables++; return false;
});
}
node->for_each_callback_group(
[&number_of_entities](rclcpp::CallbackGroup::SharedPtr group)
{
if (!group->can_be_taken_from().load()) {
return;
}
group->find_subscription_ptrs_if(
[&number_of_entities](rclcpp::SubscriptionBase::SharedPtr &)
{
number_of_entities->subscriptions++; return false;
});
group->find_timer_ptrs_if(
[&number_of_entities](rclcpp::TimerBase::SharedPtr &)
{
number_of_entities->timers++; return false;
});
group->find_service_ptrs_if(
[&number_of_entities](rclcpp::ServiceBase::SharedPtr &)
{
number_of_entities->services++; return false;
});
group->find_client_ptrs_if(
[&number_of_entities](rclcpp::ClientBase::SharedPtr &)
{
number_of_entities->clients++; return false;
});
group->find_waitable_ptrs_if(
[&number_of_entities](rclcpp::Waitable::SharedPtr &)
{
number_of_entities->waitables++; return false;
});
});
return number_of_entities;
}
@@ -138,15 +140,14 @@ TEST_F(TestStaticExecutorEntitiesCollector, init_bad_arguments) {
EXPECT_EQ(
RCL_RET_OK,
rcl_wait_set_init(&wait_set, 100, 100, 100, 100, 100, 100, context, allocator));
RCLCPP_SCOPE_EXIT({EXPECT_EQ(RCL_RET_OK, rcl_wait_set_fini(&wait_set));});
RCPPUTILS_SCOPE_EXIT({EXPECT_EQ(RCL_RET_OK, rcl_wait_set_fini(&wait_set));});
rclcpp::GuardCondition guard_condition(shared_context);
rcl_guard_condition_t rcl_guard_condition = guard_condition.get_rcl_guard_condition();
// Check memory strategy is nullptr
rclcpp::memory_strategy::MemoryStrategy::SharedPtr memory_strategy = nullptr;
EXPECT_THROW(
entities_collector_->init(&wait_set, memory_strategy, &rcl_guard_condition),
entities_collector_->init(&wait_set, memory_strategy),
std::runtime_error);
}
@@ -163,14 +164,13 @@ TEST_F(TestStaticExecutorEntitiesCollector, add_remove_basic_node) {
EXPECT_EQ(
RCL_RET_OK,
rcl_wait_set_init(&wait_set, 100, 100, 100, 100, 100, 100, context, allocator));
RCLCPP_SCOPE_EXIT({EXPECT_EQ(RCL_RET_OK, rcl_wait_set_fini(&wait_set));});
RCPPUTILS_SCOPE_EXIT({EXPECT_EQ(RCL_RET_OK, rcl_wait_set_fini(&wait_set));});
auto memory_strategy = rclcpp::memory_strategies::create_default_strategy();
rclcpp::GuardCondition guard_condition(shared_context);
rcl_guard_condition_t rcl_guard_condition = guard_condition.get_rcl_guard_condition();
entities_collector_->init(&wait_set, memory_strategy, &rcl_guard_condition);
RCLCPP_SCOPE_EXIT(entities_collector_->fini());
entities_collector_->init(&wait_set, memory_strategy);
RCPPUTILS_SCOPE_EXIT(entities_collector_->fini());
EXPECT_EQ(
expected_number_of_entities->subscriptions,
entities_collector_->get_number_of_subscriptions());
@@ -183,7 +183,7 @@ TEST_F(TestStaticExecutorEntitiesCollector, add_remove_basic_node) {
entities_collector_->get_number_of_waitables());
EXPECT_TRUE(entities_collector_->remove_node(node->get_node_base_interface()));
entities_collector_->init(&wait_set, memory_strategy, &rcl_guard_condition);
entities_collector_->init(&wait_set, memory_strategy);
EXPECT_EQ(0u, entities_collector_->get_number_of_subscriptions());
EXPECT_EQ(0u, entities_collector_->get_number_of_timers());
EXPECT_EQ(0u, entities_collector_->get_number_of_services());
@@ -210,15 +210,14 @@ TEST_F(TestStaticExecutorEntitiesCollector, add_remove_node_out_of_scope) {
EXPECT_EQ(
RCL_RET_OK,
rcl_wait_set_init(&wait_set, 100, 100, 100, 100, 100, 100, context, allocator));
RCLCPP_SCOPE_EXIT({EXPECT_EQ(RCL_RET_OK, rcl_wait_set_fini(&wait_set));});
RCPPUTILS_SCOPE_EXIT({EXPECT_EQ(RCL_RET_OK, rcl_wait_set_fini(&wait_set));});
auto memory_strategy = rclcpp::memory_strategies::create_default_strategy();
rclcpp::GuardCondition guard_condition(shared_context);
rcl_guard_condition_t rcl_guard_condition = guard_condition.get_rcl_guard_condition();
// Expect weak_node pointers to be cleaned up and used
entities_collector_->init(&wait_set, memory_strategy, &rcl_guard_condition);
RCLCPP_SCOPE_EXIT(entities_collector_->fini());
entities_collector_->init(&wait_set, memory_strategy);
RCPPUTILS_SCOPE_EXIT(entities_collector_->fini());
EXPECT_EQ(0u, entities_collector_->get_number_of_subscriptions());
EXPECT_EQ(0u, entities_collector_->get_number_of_timers());
EXPECT_EQ(0u, entities_collector_->get_number_of_services());
@@ -281,15 +280,14 @@ TEST_F(TestStaticExecutorEntitiesCollector, add_remove_node_with_entities) {
EXPECT_EQ(
RCL_RET_OK,
rcl_wait_set_init(&wait_set, 100, 100, 100, 100, 100, 100, context, allocator));
RCLCPP_SCOPE_EXIT({EXPECT_EQ(RCL_RET_OK, rcl_wait_set_fini(&wait_set));});
RCPPUTILS_SCOPE_EXIT({EXPECT_EQ(RCL_RET_OK, rcl_wait_set_fini(&wait_set));});
auto memory_strategy = rclcpp::memory_strategies::create_default_strategy();
rclcpp::GuardCondition guard_condition(shared_context);
rcl_guard_condition_t rcl_guard_condition = guard_condition.get_rcl_guard_condition();
entities_collector_->init(&wait_set, memory_strategy, &rcl_guard_condition);
RCLCPP_SCOPE_EXIT(entities_collector_->fini());
entities_collector_->init(&wait_set, memory_strategy);
RCPPUTILS_SCOPE_EXIT(entities_collector_->fini());
EXPECT_EQ(
1u + expected_number_of_entities->subscriptions,
@@ -308,7 +306,7 @@ TEST_F(TestStaticExecutorEntitiesCollector, add_remove_node_with_entities) {
entities_collector_->get_number_of_waitables());
entities_collector_->remove_node(node->get_node_base_interface());
entities_collector_->init(&wait_set, memory_strategy, &rcl_guard_condition);
entities_collector_->init(&wait_set, memory_strategy);
EXPECT_EQ(0u, entities_collector_->get_number_of_subscriptions());
EXPECT_EQ(0u, entities_collector_->get_number_of_timers());
EXPECT_EQ(0u, entities_collector_->get_number_of_services());
@@ -361,14 +359,13 @@ TEST_F(TestStaticExecutorEntitiesCollector, prepare_wait_set_rcl_wait_set_clear_
EXPECT_EQ(
RCL_RET_OK,
rcl_wait_set_init(&wait_set, 100, 100, 100, 100, 100, 100, context, allocator));
RCLCPP_SCOPE_EXIT({EXPECT_EQ(RCL_RET_OK, rcl_wait_set_fini(&wait_set));});
RCPPUTILS_SCOPE_EXIT({EXPECT_EQ(RCL_RET_OK, rcl_wait_set_fini(&wait_set));});
auto memory_strategy = rclcpp::memory_strategies::create_default_strategy();
rclcpp::GuardCondition guard_condition(shared_context);
rcl_guard_condition_t rcl_guard_condition = guard_condition.get_rcl_guard_condition();
entities_collector_->init(&wait_set, memory_strategy, &rcl_guard_condition);
RCLCPP_SCOPE_EXIT(entities_collector_->fini());
entities_collector_->init(&wait_set, memory_strategy);
RCPPUTILS_SCOPE_EXIT(entities_collector_->fini());
{
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_wait_set_clear, RCL_RET_ERROR);
@@ -392,14 +389,13 @@ TEST_F(TestStaticExecutorEntitiesCollector, prepare_wait_set_rcl_wait_set_resize
EXPECT_EQ(
RCL_RET_OK,
rcl_wait_set_init(&wait_set, 100, 100, 100, 100, 100, 100, context, allocator));
RCLCPP_SCOPE_EXIT({EXPECT_EQ(RCL_RET_OK, rcl_wait_set_fini(&wait_set));});
RCPPUTILS_SCOPE_EXIT({EXPECT_EQ(RCL_RET_OK, rcl_wait_set_fini(&wait_set));});
auto memory_strategy = rclcpp::memory_strategies::create_default_strategy();
rclcpp::GuardCondition guard_condition(shared_context);
rcl_guard_condition_t rcl_guard_condition = guard_condition.get_rcl_guard_condition();
entities_collector_->init(&wait_set, memory_strategy, &rcl_guard_condition);
RCLCPP_SCOPE_EXIT(entities_collector_->fini());
entities_collector_->init(&wait_set, memory_strategy);
RCPPUTILS_SCOPE_EXIT(entities_collector_->fini());
{
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_wait_set_resize, RCL_RET_ERROR);
@@ -430,14 +426,13 @@ TEST_F(TestStaticExecutorEntitiesCollector, refresh_wait_set_rcl_wait_failed) {
EXPECT_EQ(
RCL_RET_OK,
rcl_wait_set_init(&wait_set, 100, 100, 100, 100, 100, 100, context, allocator));
RCLCPP_SCOPE_EXIT({EXPECT_EQ(RCL_RET_OK, rcl_wait_set_fini(&wait_set));});
RCPPUTILS_SCOPE_EXIT({EXPECT_EQ(RCL_RET_OK, rcl_wait_set_fini(&wait_set));});
auto memory_strategy = rclcpp::memory_strategies::create_default_strategy();
rclcpp::GuardCondition guard_condition(shared_context);
rcl_guard_condition_t rcl_guard_condition = guard_condition.get_rcl_guard_condition();
entities_collector_->init(&wait_set, memory_strategy, &rcl_guard_condition);
RCLCPP_SCOPE_EXIT(entities_collector_->fini());
entities_collector_->init(&wait_set, memory_strategy);
RCPPUTILS_SCOPE_EXIT(entities_collector_->fini());
{
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_wait, RCL_RET_ERROR);
@@ -478,15 +473,14 @@ TEST_F(TestStaticExecutorEntitiesCollector, refresh_wait_set_add_handles_to_wait
EXPECT_EQ(
RCL_RET_OK,
rcl_wait_set_init(&wait_set, 100, 100, 100, 100, 100, 100, context, allocator));
RCLCPP_SCOPE_EXIT({EXPECT_EQ(RCL_RET_OK, rcl_wait_set_fini(&wait_set));});
RCPPUTILS_SCOPE_EXIT({EXPECT_EQ(RCL_RET_OK, rcl_wait_set_fini(&wait_set));});
auto memory_strategy = rclcpp::memory_strategies::create_default_strategy();
rclcpp::GuardCondition guard_condition(shared_context);
rcl_guard_condition_t rcl_guard_condition = guard_condition.get_rcl_guard_condition();
entities_collector_->init(&wait_set, memory_strategy, &rcl_guard_condition);
RCLCPP_SCOPE_EXIT(entities_collector_->fini());
entities_collector_->init(&wait_set, memory_strategy);
RCPPUTILS_SCOPE_EXIT(entities_collector_->fini());
{
auto mock = mocking_utils::patch_and_return(
@@ -511,14 +505,13 @@ TEST_F(TestStaticExecutorEntitiesCollector, add_to_wait_set_nullptr) {
EXPECT_EQ(
RCL_RET_OK,
rcl_wait_set_init(&wait_set, 100, 100, 100, 100, 100, 100, context, allocator));
RCLCPP_SCOPE_EXIT({EXPECT_EQ(RCL_RET_OK, rcl_wait_set_fini(&wait_set));});
RCPPUTILS_SCOPE_EXIT({EXPECT_EQ(RCL_RET_OK, rcl_wait_set_fini(&wait_set));});
auto memory_strategy = rclcpp::memory_strategies::create_default_strategy();
rclcpp::GuardCondition guard_condition(shared_context);
rcl_guard_condition_t rcl_guard_condition = guard_condition.get_rcl_guard_condition();
entities_collector_->init(&wait_set, memory_strategy, &rcl_guard_condition);
RCLCPP_SCOPE_EXIT(entities_collector_->fini());
entities_collector_->init(&wait_set, memory_strategy);
RCPPUTILS_SCOPE_EXIT(entities_collector_->fini());
RCLCPP_EXPECT_THROW_EQ(
entities_collector_->add_to_wait_set(nullptr),
@@ -539,11 +532,10 @@ TEST_F(TestStaticExecutorEntitiesCollector, fill_memory_strategy_invalid_group)
EXPECT_EQ(
RCL_RET_OK,
rcl_wait_set_init(&wait_set, 100, 100, 100, 100, 100, 100, context, allocator));
RCLCPP_SCOPE_EXIT({EXPECT_EQ(RCL_RET_OK, rcl_wait_set_fini(&wait_set));});
RCPPUTILS_SCOPE_EXIT({EXPECT_EQ(RCL_RET_OK, rcl_wait_set_fini(&wait_set));});
auto memory_strategy = rclcpp::memory_strategies::create_default_strategy();
rclcpp::GuardCondition guard_condition(shared_context);
rcl_guard_condition_t rcl_guard_condition = guard_condition.get_rcl_guard_condition();
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive);
@@ -553,8 +545,8 @@ TEST_F(TestStaticExecutorEntitiesCollector, fill_memory_strategy_invalid_group)
cb_group.reset();
entities_collector_->init(&wait_set, memory_strategy, &rcl_guard_condition);
RCLCPP_SCOPE_EXIT(entities_collector_->fini());
entities_collector_->init(&wait_set, memory_strategy);
RCPPUTILS_SCOPE_EXIT(entities_collector_->fini());
ASSERT_EQ(entities_collector_->get_all_callback_groups().size(), 1u);
EXPECT_TRUE(entities_collector_->remove_node(node->get_node_base_interface()));
@@ -617,14 +609,13 @@ TEST_F(
EXPECT_EQ(
RCL_RET_OK,
rcl_wait_set_init(&wait_set, 100, 100, 100, 100, 100, 100, context, allocator));
RCLCPP_SCOPE_EXIT({EXPECT_EQ(RCL_RET_OK, rcl_wait_set_fini(&wait_set));});
RCPPUTILS_SCOPE_EXIT({EXPECT_EQ(RCL_RET_OK, rcl_wait_set_fini(&wait_set));});
auto memory_strategy = rclcpp::memory_strategies::create_default_strategy();
rclcpp::GuardCondition guard_condition(shared_context);
rcl_guard_condition_t rcl_guard_condition = guard_condition.get_rcl_guard_condition();
entities_collector_->init(&wait_set, memory_strategy, &rcl_guard_condition);
RCLCPP_SCOPE_EXIT(entities_collector_->fini());
entities_collector_->init(&wait_set, memory_strategy);
RCPPUTILS_SCOPE_EXIT(entities_collector_->fini());
cb_group->get_associated_with_executor_atomic().exchange(false);
std::shared_ptr<void> data = entities_collector_->take_data();

View File

@@ -32,6 +32,7 @@ public:
: TimerBase(node->get_clock(), std::chrono::nanoseconds(1),
node->get_node_base_interface()->get_context()) {}
bool call() override {return true;}
void execute_callback() override {}
bool is_steady() override {return false;}
};

View File

@@ -71,6 +71,8 @@ public:
void handle_message(std::shared_ptr<void> &, const rclcpp::MessageInfo &) override {}
void handle_loaned_message(void *, const rclcpp::MessageInfo &) override {}
void handle_serialized_message(
const std::shared_ptr<rclcpp::SerializedMessage> &, const rclcpp::MessageInfo &) override {}
void return_message(std::shared_ptr<void> &) override {}
void return_serialized_message(std::shared_ptr<rclcpp::SerializedMessage> &) override {}
};

View File

@@ -21,8 +21,8 @@
#include "gtest/gtest.h"
#include "rclcpp/scope_exit.hpp"
#include "rclcpp/strategies/allocator_memory_strategy.hpp"
#include "rcpputils/scope_exit.hpp"
#include "test_msgs/msg/empty.hpp"
#include "test_msgs/srv/empty.hpp"
@@ -114,12 +114,11 @@ protected:
{
auto node = std::make_shared<rclcpp::Node>(name, "ns");
for (auto & group_weak_ptr : node->get_callback_groups()) {
auto group = group_weak_ptr.lock();
if (group) {
node->for_each_callback_group(
[](rclcpp::CallbackGroup::SharedPtr group)
{
group->can_be_taken_from() = false;
}
}
});
return node;
}
@@ -201,15 +200,13 @@ protected:
const RclWaitSetSizes & expected)
{
WeakCallbackGroupsToNodesMap weak_groups_to_nodes;
auto callback_groups = node->get_node_base_interface()->get_callback_groups();
std::for_each(
callback_groups.begin(), callback_groups.end(),
[&weak_groups_to_nodes,
&node](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
node->for_each_callback_group(
[node, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
{
weak_groups_to_nodes.insert(
std::pair<rclcpp::CallbackGroup::WeakPtr,
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
weak_group_ptr,
group_ptr,
node->get_node_base_interface()));
});
allocator_memory_strategy()->collect_entities(weak_groups_to_nodes);
@@ -234,15 +231,13 @@ protected:
const RclWaitSetSizes & insufficient_capacities)
{
WeakCallbackGroupsToNodesMap weak_groups_to_nodes;
auto callback_groups = node->get_node_base_interface()->get_callback_groups();
std::for_each(
callback_groups.begin(), callback_groups.end(),
[&weak_groups_to_nodes,
&node](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
node->for_each_callback_group(
[node, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
{
weak_groups_to_nodes.insert(
std::pair<rclcpp::CallbackGroup::WeakPtr,
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
weak_group_ptr,
group_ptr,
node->get_node_base_interface()));
});
allocator_memory_strategy()->collect_entities(weak_groups_to_nodes);
@@ -268,7 +263,7 @@ protected:
"Calling rcl_wait_set_init() with expected sufficient capacities failed";
}
RCLCPP_SCOPE_EXIT(
RCPPUTILS_SCOPE_EXIT(
{
EXPECT_EQ(RCL_RET_OK, rcl_wait_set_fini(&wait_set));
});
@@ -296,7 +291,7 @@ protected:
"Calling rcl_wait_set_init() with expected insufficient capacities failed";
}
RCLCPP_SCOPE_EXIT(
RCPPUTILS_SCOPE_EXIT(
{
EXPECT_EQ(RCL_RET_OK, rcl_wait_set_fini(&wait_set_no_capacity));
});
@@ -316,26 +311,22 @@ protected:
{
auto basic_node = create_node_with_disabled_callback_groups("basic_node");
WeakCallbackGroupsToNodesMap weak_groups_to_nodes;
auto callback_groups = basic_node->get_node_base_interface()->get_callback_groups();
std::for_each(
callback_groups.begin(), callback_groups.end(),
[&weak_groups_to_nodes,
&basic_node](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
basic_node->for_each_callback_group(
[basic_node, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
{
weak_groups_to_nodes.insert(
std::pair<rclcpp::CallbackGroup::WeakPtr,
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
weak_group_ptr,
group_ptr,
basic_node->get_node_base_interface()));
});
auto callback_groups1 = node_with_entity1->get_node_base_interface()->get_callback_groups();
std::for_each(
callback_groups1.begin(), callback_groups1.end(),
[&weak_groups_to_nodes,
&node_with_entity1](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
node_with_entity1->for_each_callback_group(
[node_with_entity1, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
{
weak_groups_to_nodes.insert(
std::pair<rclcpp::CallbackGroup::WeakPtr,
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
weak_group_ptr,
group_ptr,
node_with_entity1->get_node_base_interface()));
});
allocator_memory_strategy()->collect_entities(weak_groups_to_nodes);
@@ -348,26 +339,23 @@ protected:
auto basic_node2 = std::make_shared<rclcpp::Node>("basic_node2", "ns");
WeakCallbackGroupsToNodesMap weak_groups_to_uncollected_nodes;
auto callback_groups2 = basic_node2->get_node_base_interface()->get_callback_groups();
std::for_each(
callback_groups2.begin(), callback_groups2.end(),
[&weak_groups_to_uncollected_nodes,
&basic_node2](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
basic_node2->for_each_callback_group(
[basic_node2, &weak_groups_to_uncollected_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
{
weak_groups_to_uncollected_nodes.insert(
std::pair<rclcpp::CallbackGroup::WeakPtr,
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
weak_group_ptr,
group_ptr,
basic_node2->get_node_base_interface()));
});
auto callback_groups3 = node_with_entity2->get_node_base_interface()->get_callback_groups();
std::for_each(
callback_groups3.begin(), callback_groups3.end(),
[&weak_groups_to_uncollected_nodes,
&node_with_entity2](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
node_with_entity2->for_each_callback_group(
[node_with_entity2,
&weak_groups_to_uncollected_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
{
weak_groups_to_uncollected_nodes.insert(
std::pair<rclcpp::CallbackGroup::WeakPtr,
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
weak_group_ptr,
group_ptr,
node_with_entity2->get_node_base_interface()));
});
rclcpp::AnyExecutable failed_result = get_next_entity_func(weak_groups_to_uncollected_nodes);
@@ -388,24 +376,22 @@ protected:
auto basic_node_base = basic_node->get_node_base_interface();
auto node_base = node_with_entity->get_node_base_interface();
WeakCallbackGroupsToNodesMap weak_groups_to_nodes;
auto callback_groups = basic_node_base->get_callback_groups();
std::for_each(
callback_groups.begin(), callback_groups.end(),
[&weak_groups_to_nodes, &basic_node_base](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
basic_node_base->for_each_callback_group(
[basic_node_base, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
{
weak_groups_to_nodes.insert(
std::pair<rclcpp::CallbackGroup::WeakPtr,
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
weak_group_ptr,
group_ptr,
basic_node_base));
});
callback_groups = node_base->get_callback_groups();
std::for_each(
callback_groups.begin(), callback_groups.end(),
[&weak_groups_to_nodes, &node_base](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
node_base->for_each_callback_group(
[node_base, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
{
weak_groups_to_nodes.insert(
std::pair<rclcpp::CallbackGroup::WeakPtr,
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
weak_group_ptr,
group_ptr,
node_base));
});
allocator_memory_strategy()->collect_entities(weak_groups_to_nodes);
@@ -447,14 +433,13 @@ private:
TEST_F(TestAllocatorMemoryStrategy, construct_destruct) {
auto basic_node = create_node_with_disabled_callback_groups("basic_node");
WeakCallbackGroupsToNodesMap weak_groups_to_nodes;
auto callback_groups = basic_node->get_node_base_interface()->get_callback_groups();
std::for_each(
callback_groups.begin(), callback_groups.end(),
[&weak_groups_to_nodes, &basic_node](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
basic_node->for_each_callback_group(
[basic_node, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
{
weak_groups_to_nodes.insert(
std::pair<rclcpp::CallbackGroup::WeakPtr,
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
weak_group_ptr,
group_ptr,
basic_node->get_node_base_interface()));
});
allocator_memory_strategy()->collect_entities(weak_groups_to_nodes);
@@ -546,14 +531,13 @@ TEST_F(TestAllocatorMemoryStrategy, number_of_entities_with_timer) {
TEST_F(TestAllocatorMemoryStrategy, add_handles_to_wait_set_bad_arguments) {
auto node = create_node_with_subscription("subscription_node");
WeakCallbackGroupsToNodesMap weak_groups_to_nodes;
auto callback_groups = node->get_node_base_interface()->get_callback_groups();
std::for_each(
callback_groups.begin(), callback_groups.end(),
[&weak_groups_to_nodes, &node](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
node->for_each_callback_group(
[node, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
{
weak_groups_to_nodes.insert(
std::pair<rclcpp::CallbackGroup::WeakPtr,
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
weak_group_ptr,
group_ptr,
node->get_node_base_interface()));
});
allocator_memory_strategy()->collect_entities(weak_groups_to_nodes);
@@ -594,7 +578,7 @@ TEST_F(TestAllocatorMemoryStrategy, add_handles_to_wait_set_guard_condition) {
EXPECT_EQ(
RCL_RET_OK,
rcl_guard_condition_init(&guard_condition, rcl_context, guard_condition_options));
RCLCPP_SCOPE_EXIT(
RCPPUTILS_SCOPE_EXIT(
{
EXPECT_EQ(RCL_RET_OK, rcl_guard_condition_fini(&guard_condition));
});
@@ -787,14 +771,13 @@ TEST_F(TestAllocatorMemoryStrategy, get_next_subscription_out_of_scope) {
test_msgs::msg::Empty, decltype(subscription_callback)>(
"topic", qos, std::move(subscription_callback), subscription_options);
auto callback_groups = node->get_node_base_interface()->get_callback_groups();
std::for_each(
callback_groups.begin(), callback_groups.end(),
[&weak_groups_to_nodes, &node](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
node->for_each_callback_group(
[node, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
{
weak_groups_to_nodes.insert(
std::pair<rclcpp::CallbackGroup::WeakPtr,
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
weak_group_ptr,
group_ptr,
node->get_node_base_interface()));
});
allocator_memory_strategy()->collect_entities(weak_groups_to_nodes);
@@ -821,14 +804,13 @@ TEST_F(TestAllocatorMemoryStrategy, get_next_service_out_of_scope) {
auto service = node->create_service<test_msgs::srv::Empty>(
"service", std::move(service_callback), rmw_qos_profile_services_default, callback_group);
auto callback_groups = node->get_node_base_interface()->get_callback_groups();
std::for_each(
callback_groups.begin(), callback_groups.end(),
[&weak_groups_to_nodes, &node](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
node->for_each_callback_group(
[node, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
{
weak_groups_to_nodes.insert(
std::pair<rclcpp::CallbackGroup::WeakPtr,
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
weak_group_ptr,
group_ptr,
node->get_node_base_interface()));
});
allocator_memory_strategy()->collect_entities(weak_groups_to_nodes);
@@ -843,14 +825,13 @@ TEST_F(TestAllocatorMemoryStrategy, get_next_service_out_of_scope) {
TEST_F(TestAllocatorMemoryStrategy, get_next_client_out_of_scope) {
auto node = create_node_with_disabled_callback_groups("node");
WeakCallbackGroupsToNodesMap weak_groups_to_nodes;
auto callback_groups = node->get_node_base_interface()->get_callback_groups();
std::for_each(
callback_groups.begin(), callback_groups.end(),
[&weak_groups_to_nodes, &node](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
node->for_each_callback_group(
[node, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
{
weak_groups_to_nodes.insert(
std::pair<rclcpp::CallbackGroup::WeakPtr,
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
weak_group_ptr,
group_ptr,
node->get_node_base_interface()));
});
// Force client to go out of scope and cleanup after collecting entities.
@@ -886,14 +867,13 @@ TEST_F(TestAllocatorMemoryStrategy, get_next_timer_out_of_scope) {
rclcpp::CallbackGroupType::MutuallyExclusive);
auto timer = node->create_wall_timer(
std::chrono::seconds(10), []() {}, callback_group);
auto callback_groups = node->get_node_base_interface()->get_callback_groups();
std::for_each(
callback_groups.begin(), callback_groups.end(),
[&weak_groups_to_nodes, &node](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
node->for_each_callback_group(
[node, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
{
weak_groups_to_nodes.insert(
std::pair<rclcpp::CallbackGroup::WeakPtr,
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
weak_group_ptr,
group_ptr,
node->get_node_base_interface()));
});
allocator_memory_strategy()->collect_entities(weak_groups_to_nodes);
@@ -913,14 +893,13 @@ TEST_F(TestAllocatorMemoryStrategy, get_next_waitable_out_of_scope) {
auto callback_group =
node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive);
auto callback_groups = node->get_node_base_interface()->get_callback_groups();
std::for_each(
callback_groups.begin(), callback_groups.end(),
[&weak_groups_to_nodes, &node](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
node->for_each_callback_group(
[node, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
{
weak_groups_to_nodes.insert(
std::pair<rclcpp::CallbackGroup::WeakPtr,
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
weak_group_ptr,
group_ptr,
node->get_node_base_interface()));
});
allocator_memory_strategy()->collect_entities(weak_groups_to_nodes);

View File

@@ -22,6 +22,7 @@
#include <utility>
#include "rclcpp/any_service_callback.hpp"
#include "rclcpp/service.hpp"
#include "test_msgs/srv/empty.hpp"
#include "test_msgs/srv/empty.h"
@@ -44,7 +45,7 @@ protected:
TEST_F(TestAnyServiceCallback, no_set_and_dispatch_throw) {
EXPECT_THROW(
any_service_callback_.dispatch(request_header_, request_, response_),
any_service_callback_.dispatch(nullptr, request_header_, request_),
std::runtime_error);
}
@@ -57,7 +58,7 @@ TEST_F(TestAnyServiceCallback, set_and_dispatch_no_header) {
any_service_callback_.set(callback);
EXPECT_NO_THROW(
any_service_callback_.dispatch(request_header_, request_, response_));
EXPECT_NE(nullptr, any_service_callback_.dispatch(nullptr, request_header_, request_)));
EXPECT_EQ(callback_calls, 1);
}
@@ -73,6 +74,36 @@ TEST_F(TestAnyServiceCallback, set_and_dispatch_header) {
any_service_callback_.set(callback_with_header);
EXPECT_NO_THROW(
any_service_callback_.dispatch(request_header_, request_, response_));
EXPECT_NE(nullptr, any_service_callback_.dispatch(nullptr, request_header_, request_)));
EXPECT_EQ(callback_with_header_calls, 1);
}
TEST_F(TestAnyServiceCallback, set_and_dispatch_defered) {
int callback_with_header_calls = 0;
auto callback_with_header =
[&callback_with_header_calls](const std::shared_ptr<rmw_request_id_t>,
const std::shared_ptr<test_msgs::srv::Empty::Request>) {
callback_with_header_calls++;
};
any_service_callback_.set(callback_with_header);
EXPECT_NO_THROW(
EXPECT_EQ(nullptr, any_service_callback_.dispatch(nullptr, request_header_, request_)));
EXPECT_EQ(callback_with_header_calls, 1);
}
TEST_F(TestAnyServiceCallback, set_and_dispatch_defered_with_service_handle) {
int callback_with_header_calls = 0;
auto callback_with_header =
[&callback_with_header_calls](std::shared_ptr<rclcpp::Service<test_msgs::srv::Empty>>,
const std::shared_ptr<rmw_request_id_t>,
const std::shared_ptr<test_msgs::srv::Empty::Request>)
{
callback_with_header_calls++;
};
any_service_callback_.set(callback_with_header);
EXPECT_NO_THROW(
EXPECT_EQ(nullptr, any_service_callback_.dispatch(nullptr, request_header_, request_)));
EXPECT_EQ(callback_with_header_calls, 1);
}

View File

@@ -67,7 +67,7 @@ void construct_with_null_allocator()
TEST(AnySubscriptionCallback, null_allocator) {
EXPECT_THROW(
construct_with_null_allocator(),
std::runtime_error);
std::invalid_argument);
}
TEST_F(TestAnySubscriptionCallback, construct_destruct) {
@@ -95,44 +95,69 @@ TEST_F(TestAnySubscriptionCallback, unset_dispatch_throw) {
// Parameterized test to test across all callback types and dispatch types.
//
// Type adapter to be used in tests.
struct MyEmpty {};
template<>
struct rclcpp::TypeAdapter<MyEmpty, test_msgs::msg::Empty>
{
using is_specialized = std::true_type;
using custom_type = MyEmpty;
using ros_message_type = test_msgs::msg::Empty;
static
void
convert_to_ros_message(const custom_type &, ros_message_type &)
{}
static
void
convert_to_custom(const ros_message_type &, custom_type &)
{}
};
using MyTA = rclcpp::TypeAdapter<MyEmpty, test_msgs::msg::Empty>;
template<typename MessageT>
class InstanceContextImpl
{
public:
InstanceContextImpl() = default;
virtual ~InstanceContextImpl() = default;
explicit InstanceContextImpl(rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty> asc)
explicit InstanceContextImpl(rclcpp::AnySubscriptionCallback<MessageT> asc)
: any_subscription_callback_(asc)
{}
virtual
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>
rclcpp::AnySubscriptionCallback<MessageT>
get_any_subscription_callback_to_test() const
{
return any_subscription_callback_;
}
protected:
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty> any_subscription_callback_;
rclcpp::AnySubscriptionCallback<MessageT> any_subscription_callback_;
};
template<typename MessageT>
class InstanceContext
{
public:
InstanceContext(const std::string & name, std::shared_ptr<InstanceContextImpl> impl)
InstanceContext(const std::string & name, std::shared_ptr<InstanceContextImpl<MessageT>> impl)
: name(name), impl_(impl)
{}
InstanceContext(
const std::string & name,
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty> asc)
: name(name), impl_(std::make_shared<InstanceContextImpl>(asc))
rclcpp::AnySubscriptionCallback<MessageT> asc)
: name(name), impl_(std::make_shared<InstanceContextImpl<MessageT>>(asc))
{}
InstanceContext(const InstanceContext & other)
: InstanceContext(other.name, other.impl_) {}
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>
rclcpp::AnySubscriptionCallback<MessageT>
get_any_subscription_callback_to_test() const
{
return impl_->get_any_subscription_callback_to_test();
@@ -141,12 +166,17 @@ public:
std::string name;
protected:
std::shared_ptr<InstanceContextImpl> impl_;
std::shared_ptr<InstanceContextImpl<MessageT>> impl_;
};
class DispatchTests
: public TestAnySubscriptionCallback,
public ::testing::WithParamInterface<InstanceContext>
public ::testing::WithParamInterface<InstanceContext<test_msgs::msg::Empty>>
{};
class DispatchTestsWithTA
: public TestAnySubscriptionCallback,
public ::testing::WithParamInterface<InstanceContext<MyTA>>
{};
auto
@@ -155,57 +185,67 @@ format_parameter(const ::testing::TestParamInfo<DispatchTests::ParamType> & info
return info.param.name;
}
// Testing dispatch with shared_ptr<MessageT> as input
TEST_P(DispatchTests, test_inter_shared_dispatch) {
auto any_subscription_callback_to_test = GetParam().get_any_subscription_callback_to_test();
any_subscription_callback_to_test.dispatch(msg_shared_ptr_, message_info_);
auto
format_parameter_with_ta(const ::testing::TestParamInfo<DispatchTestsWithTA::ParamType> & info)
{
return info.param.name;
}
// Testing dispatch with shared_ptr<const MessageT> as input
TEST_P(DispatchTests, test_intra_shared_dispatch) {
auto any_subscription_callback_to_test = GetParam().get_any_subscription_callback_to_test();
any_subscription_callback_to_test.dispatch_intra_process(msg_shared_ptr_, message_info_);
}
#define PARAMETERIZED_TESTS(DispatchTests_name) \
/* Testing dispatch with shared_ptr<MessageT> as input */ \
TEST_P(DispatchTests_name, test_inter_shared_dispatch) { \
auto any_subscription_callback_to_test = GetParam().get_any_subscription_callback_to_test(); \
any_subscription_callback_to_test.dispatch(msg_shared_ptr_, message_info_); \
} \
\
/* Testing dispatch with shared_ptr<const MessageT> as input */ \
TEST_P(DispatchTests_name, test_intra_shared_dispatch) { \
auto any_subscription_callback_to_test = GetParam().get_any_subscription_callback_to_test(); \
any_subscription_callback_to_test.dispatch_intra_process(msg_shared_ptr_, message_info_); \
} \
\
/* Testing dispatch with unique_ptr<MessageT> as input */ \
TEST_P(DispatchTests_name, test_intra_unique_dispatch) { \
auto any_subscription_callback_to_test = GetParam().get_any_subscription_callback_to_test(); \
any_subscription_callback_to_test.dispatch_intra_process(get_unique_ptr_msg(), message_info_); \
}
// Testing dispatch with unique_ptr<MessageT> as input
TEST_P(DispatchTests, test_intra_unique_dispatch) {
auto any_subscription_callback_to_test = GetParam().get_any_subscription_callback_to_test();
any_subscription_callback_to_test.dispatch_intra_process(get_unique_ptr_msg(), message_info_);
}
PARAMETERIZED_TESTS(DispatchTests)
PARAMETERIZED_TESTS(DispatchTestsWithTA)
// Generic classes for testing callbacks using std::bind to class methods.
template<typename ... CallbackArgs>
class BindContextImpl : public InstanceContextImpl
template<typename MessageT, typename ... CallbackArgs>
class BindContextImpl : public InstanceContextImpl<MessageT>
{
static constexpr size_t number_of_callback_args{sizeof...(CallbackArgs)};
public:
using InstanceContextImpl::InstanceContextImpl;
using InstanceContextImpl<MessageT>::InstanceContextImpl;
virtual ~BindContextImpl() = default;
void on_message(CallbackArgs ...) const {}
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>
rclcpp::AnySubscriptionCallback<MessageT>
get_any_subscription_callback_to_test() const override
{
if constexpr (number_of_callback_args == 1) {
return rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>().set(
return rclcpp::AnySubscriptionCallback<MessageT>().set(
std::bind(&BindContextImpl::on_message, this, std::placeholders::_1)
);
} else {
return rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>().set(
return rclcpp::AnySubscriptionCallback<MessageT>().set(
std::bind(&BindContextImpl::on_message, this, std::placeholders::_1, std::placeholders::_2)
);
}
}
};
template<typename ... CallbackArgs>
class BindContext : public InstanceContext
template<typename MessageT, typename ... CallbackArgs>
class BindContext : public InstanceContext<MessageT>
{
public:
explicit BindContext(const std::string & name)
: InstanceContext(name, std::make_shared<BindContextImpl<CallbackArgs ...>>())
: InstanceContext<MessageT>(name, std::make_shared<BindContextImpl<MessageT, CallbackArgs ...>>())
{}
};
@@ -232,13 +272,53 @@ INSTANTIATE_TEST_SUITE_P(
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>().set(
const_ref_w_info_free_func)},
// bind function
BindContext<const test_msgs::msg::Empty &>("bind_method"),
BindContext<const test_msgs::msg::Empty &, const rclcpp::MessageInfo &>(
BindContext<test_msgs::msg::Empty, const test_msgs::msg::Empty &>("bind_method"),
BindContext<test_msgs::msg::Empty, const test_msgs::msg::Empty &, const rclcpp::MessageInfo &>(
"bind_method_with_info")
),
format_parameter
);
void const_ref_ta_free_func(const MyEmpty &) {}
void const_ref_ta_w_info_free_func(const MyEmpty &, const rclcpp::MessageInfo &) {}
INSTANTIATE_TEST_SUITE_P(
ConstRefTACallbackTests,
DispatchTestsWithTA,
::testing::Values(
// lambda
InstanceContext<MyTA>{"lambda_ta", rclcpp::AnySubscriptionCallback<MyTA>().set(
[](const MyEmpty &) {})},
InstanceContext<MyTA>{"lambda_ta_with_info",
rclcpp::AnySubscriptionCallback<MyTA>().set(
[](const MyEmpty &, const rclcpp::MessageInfo &) {})},
InstanceContext<MyTA>{"lambda", rclcpp::AnySubscriptionCallback<MyTA>().set(
[](const test_msgs::msg::Empty &) {})},
InstanceContext<MyTA>{"lambda_with_info",
rclcpp::AnySubscriptionCallback<MyTA>().set(
[](const test_msgs::msg::Empty &, const rclcpp::MessageInfo &) {})},
// free function
InstanceContext<MyTA>{"free_function_ta", rclcpp::AnySubscriptionCallback<MyTA>().set(
const_ref_ta_free_func)},
InstanceContext<MyTA>{"free_function_ta_with_info",
rclcpp::AnySubscriptionCallback<MyTA>().set(
const_ref_ta_w_info_free_func)},
InstanceContext<MyTA>{"free_function", rclcpp::AnySubscriptionCallback<MyTA>().set(
const_ref_free_func)},
InstanceContext<MyTA>{"free_function_with_info",
rclcpp::AnySubscriptionCallback<MyTA>().set(
const_ref_w_info_free_func)},
// bind function
BindContext<MyTA, const MyEmpty &>("bind_method_ta"),
BindContext<MyTA, const MyEmpty &, const rclcpp::MessageInfo &>(
"bind_method_ta_with_info"),
BindContext<MyTA, const test_msgs::msg::Empty &>("bind_method"),
BindContext<MyTA, const test_msgs::msg::Empty &, const rclcpp::MessageInfo &>(
"bind_method_with_info")
),
format_parameter_with_ta
);
//
// Versions of `std::unique_ptr<MessageT, MessageDeleter>`
//
@@ -264,13 +344,56 @@ INSTANTIATE_TEST_SUITE_P(
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>().set(
unique_ptr_w_info_free_func)},
// bind function
BindContext<std::unique_ptr<test_msgs::msg::Empty>>("bind_method"),
BindContext<std::unique_ptr<test_msgs::msg::Empty>, const rclcpp::MessageInfo &>(
"bind_method_with_info")
BindContext<test_msgs::msg::Empty, std::unique_ptr<test_msgs::msg::Empty>>("bind_method"),
BindContext<
test_msgs::msg::Empty,
std::unique_ptr<test_msgs::msg::Empty>,
const rclcpp::MessageInfo &
>("bind_method_with_info")
),
format_parameter
);
void unique_ptr_ta_free_func(std::unique_ptr<MyEmpty>) {}
void unique_ptr_ta_w_info_free_func(std::unique_ptr<MyEmpty>, const rclcpp::MessageInfo &) {}
INSTANTIATE_TEST_SUITE_P(
UniquePtrCallbackTests,
DispatchTestsWithTA,
::testing::Values(
// lambda
InstanceContext<MyTA>{"lambda_ta", rclcpp::AnySubscriptionCallback<MyTA>().set(
[](std::unique_ptr<MyEmpty>) {})},
InstanceContext<MyTA>{"lambda_ta_with_info",
rclcpp::AnySubscriptionCallback<MyTA>().set(
[](std::unique_ptr<MyEmpty>, const rclcpp::MessageInfo &) {})},
InstanceContext<MyTA>{"lambda", rclcpp::AnySubscriptionCallback<MyTA>().set(
[](std::unique_ptr<test_msgs::msg::Empty>) {})},
InstanceContext<MyTA>{"lambda_with_info",
rclcpp::AnySubscriptionCallback<MyTA>().set(
[](std::unique_ptr<test_msgs::msg::Empty>, const rclcpp::MessageInfo &) {})},
// free function
InstanceContext<MyTA>{"free_function_ta", rclcpp::AnySubscriptionCallback<MyTA>().set(
unique_ptr_ta_free_func)},
InstanceContext<MyTA>{"free_function_ta_with_info",
rclcpp::AnySubscriptionCallback<MyTA>().set(
unique_ptr_ta_w_info_free_func)},
InstanceContext<MyTA>{"free_function", rclcpp::AnySubscriptionCallback<MyTA>().set(
unique_ptr_free_func)},
InstanceContext<MyTA>{"free_function_with_info",
rclcpp::AnySubscriptionCallback<MyTA>().set(
unique_ptr_w_info_free_func)},
// bind function
BindContext<MyTA, std::unique_ptr<test_msgs::msg::Empty>>("bind_method_ta"),
BindContext<MyTA, std::unique_ptr<test_msgs::msg::Empty>, const rclcpp::MessageInfo &>(
"bind_method_ta_with_info"),
BindContext<MyTA, std::unique_ptr<test_msgs::msg::Empty>>("bind_method"),
BindContext<MyTA, std::unique_ptr<test_msgs::msg::Empty>, const rclcpp::MessageInfo &>(
"bind_method_with_info")
),
format_parameter_with_ta
);
//
// Versions of `std::shared_ptr<const MessageT>`
//
@@ -296,13 +419,56 @@ INSTANTIATE_TEST_SUITE_P(
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>().set(
shared_const_ptr_w_info_free_func)},
// bind function
BindContext<std::shared_ptr<const test_msgs::msg::Empty>>("bind_method"),
BindContext<std::shared_ptr<const test_msgs::msg::Empty>, const rclcpp::MessageInfo &>(
BindContext<test_msgs::msg::Empty, std::shared_ptr<const test_msgs::msg::Empty>>("bind_method"),
BindContext<test_msgs::msg::Empty, std::shared_ptr<const test_msgs::msg::Empty>,
const rclcpp::MessageInfo &>(
"bind_method_with_info")
),
format_parameter
);
void shared_const_ptr_ta_free_func(std::shared_ptr<const MyEmpty>) {}
void shared_const_ptr_ta_w_info_free_func(
std::shared_ptr<const MyEmpty>, const rclcpp::MessageInfo &)
{}
INSTANTIATE_TEST_SUITE_P(
SharedConstPtrCallbackTests,
DispatchTestsWithTA,
::testing::Values(
// lambda
InstanceContext<MyTA>{"lambda_ta", rclcpp::AnySubscriptionCallback<MyTA>().set(
[](std::shared_ptr<const MyEmpty>) {})},
InstanceContext<MyTA>{"lambda_ta_with_info",
rclcpp::AnySubscriptionCallback<MyTA>().set(
[](std::shared_ptr<const MyEmpty>, const rclcpp::MessageInfo &) {})},
InstanceContext<MyTA>{"lambda", rclcpp::AnySubscriptionCallback<MyTA>().set(
[](std::shared_ptr<const test_msgs::msg::Empty>) {})},
InstanceContext<MyTA>{"lambda_with_info",
rclcpp::AnySubscriptionCallback<MyTA>().set(
[](std::shared_ptr<const test_msgs::msg::Empty>, const rclcpp::MessageInfo &) {})},
// free function
InstanceContext<MyTA>{"free_function_ta", rclcpp::AnySubscriptionCallback<MyTA>().set(
shared_const_ptr_ta_free_func)},
InstanceContext<MyTA>{"free_function_ta_with_info",
rclcpp::AnySubscriptionCallback<MyTA>().set(
shared_const_ptr_ta_w_info_free_func)},
InstanceContext<MyTA>{"free_function", rclcpp::AnySubscriptionCallback<MyTA>().set(
shared_const_ptr_free_func)},
InstanceContext<MyTA>{"free_function_with_info",
rclcpp::AnySubscriptionCallback<MyTA>().set(
shared_const_ptr_w_info_free_func)},
// bind function
BindContext<MyTA, std::shared_ptr<const MyEmpty>>("bind_method_ta"),
BindContext<MyTA, std::shared_ptr<const MyEmpty>, const rclcpp::MessageInfo &>(
"bind_method_ta_with_info"),
BindContext<MyTA, std::shared_ptr<const test_msgs::msg::Empty>>("bind_method"),
BindContext<MyTA, std::shared_ptr<const test_msgs::msg::Empty>, const rclcpp::MessageInfo &>(
"bind_method_with_info")
),
format_parameter_with_ta
);
//
// Versions of `const std::shared_ptr<const MessageT> &`
//
@@ -328,13 +494,58 @@ INSTANTIATE_TEST_SUITE_P(
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>().set(
const_ref_shared_const_ptr_w_info_free_func)},
// bind function
BindContext<const std::shared_ptr<const test_msgs::msg::Empty> &>("bind_method"),
BindContext<const std::shared_ptr<const test_msgs::msg::Empty> &, const rclcpp::MessageInfo &>(
BindContext<test_msgs::msg::Empty,
const std::shared_ptr<const test_msgs::msg::Empty> &>("bind_method"),
BindContext<test_msgs::msg::Empty, const std::shared_ptr<const test_msgs::msg::Empty> &,
const rclcpp::MessageInfo &>(
"bind_method_with_info")
),
format_parameter
);
void const_ref_shared_const_ptr_ta_free_func(const std::shared_ptr<const MyEmpty> &) {}
void const_ref_shared_const_ptr_ta_w_info_free_func(
const std::shared_ptr<const MyEmpty> &, const rclcpp::MessageInfo &)
{}
INSTANTIATE_TEST_SUITE_P(
ConstRefSharedConstPtrCallbackTests,
DispatchTestsWithTA,
::testing::Values(
// lambda
InstanceContext<MyTA>{"lambda_ta", rclcpp::AnySubscriptionCallback<MyTA>().set(
[](const std::shared_ptr<const MyEmpty> &) {})},
InstanceContext<MyTA>{"lambda_ta_with_info",
rclcpp::AnySubscriptionCallback<MyTA>().set(
[](const std::shared_ptr<const MyEmpty> &, const rclcpp::MessageInfo &) {})},
InstanceContext<MyTA>{"lambda", rclcpp::AnySubscriptionCallback<MyTA>().set(
[](const std::shared_ptr<const test_msgs::msg::Empty> &) {})},
InstanceContext<MyTA>{"lambda_with_info",
rclcpp::AnySubscriptionCallback<MyTA>().set(
[](const std::shared_ptr<const test_msgs::msg::Empty> &, const rclcpp::MessageInfo &) {})},
// free function
InstanceContext<MyTA>{"free_function_ta", rclcpp::AnySubscriptionCallback<MyTA>().set(
const_ref_shared_const_ptr_ta_free_func)},
InstanceContext<MyTA>{"free_function_ta_with_info",
rclcpp::AnySubscriptionCallback<MyTA>().set(
const_ref_shared_const_ptr_ta_w_info_free_func)},
InstanceContext<MyTA>{"free_function", rclcpp::AnySubscriptionCallback<MyTA>().set(
const_ref_shared_const_ptr_free_func)},
InstanceContext<MyTA>{"free_function_with_info",
rclcpp::AnySubscriptionCallback<MyTA>().set(
const_ref_shared_const_ptr_w_info_free_func)},
// bind function
BindContext<MyTA, const std::shared_ptr<const MyEmpty> &>("bind_method_ta"),
BindContext<MyTA, const std::shared_ptr<const MyEmpty> &, const rclcpp::MessageInfo &>(
"bind_method_ta_with_info"),
BindContext<MyTA, const std::shared_ptr<const test_msgs::msg::Empty> &>("bind_method"),
BindContext<MyTA, const std::shared_ptr<const test_msgs::msg::Empty> &,
const rclcpp::MessageInfo &>(
"bind_method_with_info")
),
format_parameter_with_ta
);
//
// Versions of `std::shared_ptr<MessageT>`
//
@@ -360,9 +571,52 @@ INSTANTIATE_TEST_SUITE_P(
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>().set(
shared_ptr_w_info_free_func)},
// bind function
BindContext<std::shared_ptr<test_msgs::msg::Empty>>("bind_method"),
BindContext<std::shared_ptr<test_msgs::msg::Empty>, const rclcpp::MessageInfo &>(
BindContext<test_msgs::msg::Empty, std::shared_ptr<test_msgs::msg::Empty>>("bind_method"),
BindContext<test_msgs::msg::Empty, std::shared_ptr<test_msgs::msg::Empty>,
const rclcpp::MessageInfo &>(
"bind_method_with_info")
),
format_parameter
);
void shared_ptr_ta_free_func(std::shared_ptr<MyEmpty>) {}
void shared_ptr_ta_w_info_free_func(
std::shared_ptr<MyEmpty>, const rclcpp::MessageInfo &)
{}
INSTANTIATE_TEST_SUITE_P(
SharedPtrCallbackTests,
DispatchTestsWithTA,
::testing::Values(
// lambda
InstanceContext<MyTA>{"lambda_ta", rclcpp::AnySubscriptionCallback<MyTA>().set(
[](std::shared_ptr<MyEmpty>) {})},
InstanceContext<MyTA>{"lambda_ta_with_info",
rclcpp::AnySubscriptionCallback<MyTA>().set(
[](std::shared_ptr<MyEmpty>, const rclcpp::MessageInfo &) {})},
InstanceContext<MyTA>{"lambda", rclcpp::AnySubscriptionCallback<MyTA>().set(
[](std::shared_ptr<test_msgs::msg::Empty>) {})},
InstanceContext<MyTA>{"lambda_with_info",
rclcpp::AnySubscriptionCallback<MyTA>().set(
[](std::shared_ptr<test_msgs::msg::Empty>, const rclcpp::MessageInfo &) {})},
// free function
InstanceContext<MyTA>{"free_function_ta", rclcpp::AnySubscriptionCallback<MyTA>().set(
shared_ptr_ta_free_func)},
InstanceContext<MyTA>{"free_function_ta_with_info",
rclcpp::AnySubscriptionCallback<MyTA>().set(
shared_ptr_ta_w_info_free_func)},
InstanceContext<MyTA>{"free_function", rclcpp::AnySubscriptionCallback<MyTA>().set(
shared_ptr_free_func)},
InstanceContext<MyTA>{"free_function_with_info",
rclcpp::AnySubscriptionCallback<MyTA>().set(
shared_ptr_w_info_free_func)},
// bind function
BindContext<MyTA, std::shared_ptr<MyEmpty>>("bind_method_ta"),
BindContext<MyTA, std::shared_ptr<MyEmpty>, const rclcpp::MessageInfo &>(
"bind_method_ta_with_info"),
BindContext<MyTA, std::shared_ptr<test_msgs::msg::Empty>>("bind_method"),
BindContext<MyTA, std::shared_ptr<test_msgs::msg::Empty>, const rclcpp::MessageInfo &>(
"bind_method_with_info")
),
format_parameter_with_ta
);

View File

@@ -216,7 +216,7 @@ protected:
received_response = true;
};
auto future = client->async_send_request(request, std::move(callback));
auto req_id = client->async_send_request(request, std::move(callback));
auto start = std::chrono::steady_clock::now();
while (!received_response &&
@@ -228,6 +228,9 @@ protected:
if (!received_response) {
return ::testing::AssertionFailure() << "Waiting for response timed out";
}
if (client->remove_pending_request(req_id)) {
return ::testing::AssertionFailure() << "Should not be able to remove a finished request";
}
return request_result;
}
@@ -256,7 +259,7 @@ TEST_F(TestClientWithServer, async_send_request_callback_with_request) {
EXPECT_NE(nullptr, request_response_pair.second);
received_response = true;
};
auto future = client->async_send_request(request, std::move(callback));
auto req_id = client->async_send_request(request, std::move(callback));
auto start = std::chrono::steady_clock::now();
while (!received_response &&
@@ -265,6 +268,15 @@ TEST_F(TestClientWithServer, async_send_request_callback_with_request) {
rclcpp::spin_some(node);
}
EXPECT_TRUE(received_response);
EXPECT_FALSE(client->remove_pending_request(req_id));
}
TEST_F(TestClientWithServer, test_client_remove_pending_request) {
auto client = node->create_client<test_msgs::srv::Empty>("no_service_server_available_here");
auto request = std::make_shared<test_msgs::srv::Empty::Request>();
auto future = client->async_send_request(request);
EXPECT_TRUE(client->remove_pending_request(future));
}
TEST_F(TestClientWithServer, async_send_request_rcl_send_request_error) {

View File

@@ -52,8 +52,8 @@ class PublisherBase
public:
RCLCPP_SMART_PTR_DEFINITIONS(PublisherBase)
PublisherBase()
: qos(rclcpp::QoS(10)),
explicit PublisherBase(rclcpp::QoS qos = rclcpp::QoS(10))
: qos_profile(qos),
topic_name("topic")
{}
@@ -76,7 +76,7 @@ public:
rclcpp::QoS
get_actual_qos() const
{
return qos;
return qos_profile;
}
bool
@@ -93,7 +93,7 @@ public:
return false;
}
rclcpp::QoS qos;
rclcpp::QoS qos_profile;
std::string topic_name;
uint64_t intra_process_publisher_id_;
IntraProcessManagerWeakPtr weak_ipm_;
@@ -111,9 +111,9 @@ public:
RCLCPP_SMART_PTR_DEFINITIONS(Publisher<T, Alloc>)
Publisher()
explicit Publisher(rclcpp::QoS qos = rclcpp::QoS(10))
: PublisherBase(qos)
{
qos = rclcpp::QoS(10);
auto allocator = std::make_shared<Alloc>();
message_allocator_ = std::make_shared<MessageAlloc>(*allocator.get());
}
@@ -136,7 +136,10 @@ namespace buffers
{
namespace mock
{
template<typename MessageT>
template<
typename MessageT,
typename Alloc = std::allocator<void>,
typename MessageDeleter = std::default_delete<MessageT>>
class IntraProcessBuffer
{
public:
@@ -191,8 +194,8 @@ class SubscriptionIntraProcessBase
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(SubscriptionIntraProcessBase)
SubscriptionIntraProcessBase()
: qos_profile(rmw_qos_profile_default), topic_name("topic")
explicit SubscriptionIntraProcessBase(rclcpp::QoS qos = rclcpp::QoS(10))
: qos_profile(qos), topic_name("topic")
{}
virtual ~SubscriptionIntraProcessBase() {}
@@ -200,7 +203,7 @@ public:
virtual bool
use_take_shared_method() const = 0;
rmw_qos_profile_t
QoS
get_actual_qos()
{
return qos_profile;
@@ -212,18 +215,21 @@ public:
return topic_name;
}
rmw_qos_profile_t qos_profile;
rclcpp::QoS qos_profile;
const char * topic_name;
};
template<typename MessageT>
class SubscriptionIntraProcess : public SubscriptionIntraProcessBase
template<
typename MessageT,
typename Alloc = std::allocator<void>,
typename Deleter = std::default_delete<MessageT>>
class SubscriptionIntraProcessBuffer : public SubscriptionIntraProcessBase
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(SubscriptionIntraProcess)
RCLCPP_SMART_PTR_DEFINITIONS(SubscriptionIntraProcessBuffer)
SubscriptionIntraProcess()
: take_shared_method(false)
explicit SubscriptionIntraProcessBuffer(rclcpp::QoS qos)
: SubscriptionIntraProcessBase(qos), take_shared_method(false)
{
buffer = std::make_unique<rclcpp::experimental::buffers::mock::IntraProcessBuffer<MessageT>>();
}
@@ -259,6 +265,25 @@ public:
typename rclcpp::experimental::buffers::mock::IntraProcessBuffer<MessageT>::UniquePtr buffer;
};
template<
typename MessageT,
typename Alloc = std::allocator<void>,
typename Deleter = std::default_delete<MessageT>>
class SubscriptionIntraProcess : public SubscriptionIntraProcessBuffer<
MessageT,
Alloc,
Deleter
>
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(SubscriptionIntraProcess)
explicit SubscriptionIntraProcess(rclcpp::QoS qos = rclcpp::QoS(10))
: SubscriptionIntraProcessBuffer<MessageT, Alloc, Deleter>(qos)
{
}
};
} // namespace mock
} // namespace experimental
} // namespace rclcpp
@@ -267,12 +292,14 @@ public:
#define RCLCPP__PUBLISHER_HPP_
#define RCLCPP__PUBLISHER_BASE_HPP_
#define RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_HPP_
#define RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_BUFFER_HPP_
#define RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_BASE_HPP_
// Force ipm to use our mock publisher class.
#define Publisher mock::Publisher
#define PublisherBase mock::PublisherBase
#define IntraProcessBuffer mock::IntraProcessBuffer
#define SubscriptionIntraProcessBase mock::SubscriptionIntraProcessBase
#define SubscriptionIntraProcessBuffer mock::SubscriptionIntraProcessBuffer
#define SubscriptionIntraProcess mock::SubscriptionIntraProcess
#include "../src/rclcpp/intra_process_manager.cpp"
#undef Publisher
@@ -304,7 +331,7 @@ void Publisher<T, Alloc>::publish(MessageUniquePtr msg)
ipm->template do_intra_process_publish<T, Alloc>(
intra_process_publisher_id_,
std::move(msg),
message_allocator_);
*message_allocator_);
}
} // namespace mock
@@ -332,15 +359,12 @@ TEST(TestIntraProcessManager, add_pub_sub) {
auto ipm = std::make_shared<IntraProcessManagerT>();
auto p1 = std::make_shared<PublisherT>();
p1->qos.get_rmw_qos_profile().reliability = RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT;
auto p1 = std::make_shared<PublisherT>(rclcpp::QoS(10).best_effort());
auto p2 = std::make_shared<PublisherT>();
p2->qos.get_rmw_qos_profile().reliability = RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT;
auto p2 = std::make_shared<PublisherT>(rclcpp::QoS(10).best_effort());
p2->topic_name = "different_topic_name";
auto s1 = std::make_shared<SubscriptionIntraProcessT>();
s1->qos_profile.reliability = RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT;
auto s1 = std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(10).best_effort());
auto p1_id = ipm->add_publisher(p1);
auto p2_id = ipm->add_publisher(p2);
@@ -356,11 +380,9 @@ TEST(TestIntraProcessManager, add_pub_sub) {
ASSERT_EQ(0u, p2_subs);
ASSERT_EQ(0u, non_existing_pub_subs);
auto p3 = std::make_shared<PublisherT>();
p3->qos.get_rmw_qos_profile().reliability = RMW_QOS_POLICY_RELIABILITY_RELIABLE;
auto p3 = std::make_shared<PublisherT>(rclcpp::QoS(10).reliable());
auto s2 = std::make_shared<SubscriptionIntraProcessT>();
s2->qos_profile.reliability = RMW_QOS_POLICY_RELIABILITY_RELIABLE;
auto s2 = std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(10).reliable());
auto s2_id = ipm->add_subscription(s2);
auto p3_id = ipm->add_publisher(p3);

View File

@@ -0,0 +1,303 @@
// Copyright 2021 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <gmock/gmock.h>
#include <chrono>
#include <list>
#include <memory>
#include <string>
#include <type_traits>
#include <utility>
#include "test_msgs/msg/empty.hpp"
#include "rclcpp/rclcpp.hpp"
#include "rclcpp/allocator/allocator_common.hpp"
#include "rclcpp/strategies/allocator_memory_strategy.hpp"
// For demonstration purposes only, not necessary for allocator_traits
static uint32_t num_allocs = 0;
static uint32_t num_deallocs = 0;
// A very simple custom allocator. Counts calls to allocate and deallocate.
template<typename T>
struct MyAllocator
{
public:
using value_type = T;
using size_type = std::size_t;
using pointer = T *;
using const_pointer = const T *;
using difference_type = typename std::pointer_traits<pointer>::difference_type;
MyAllocator() noexcept
{
}
~MyAllocator() noexcept {}
template<typename U>
MyAllocator(const MyAllocator<U> &) noexcept
{
}
T * allocate(size_t size, const void * = 0)
{
if (size == 0) {
return nullptr;
}
num_allocs++;
return static_cast<T *>(std::malloc(size * sizeof(T)));
}
void deallocate(T * ptr, size_t size)
{
(void)size;
if (!ptr) {
return;
}
num_deallocs++;
std::free(ptr);
}
template<typename U>
struct rebind
{
typedef MyAllocator<U> other;
};
};
// Explicit specialization for void
template<>
struct MyAllocator<void>
{
public:
using value_type = void;
using pointer = void *;
using const_pointer = const void *;
MyAllocator() noexcept
{
}
~MyAllocator() noexcept {}
template<typename U>
MyAllocator(const MyAllocator<U> &) noexcept
{
}
template<typename U>
struct rebind
{
typedef MyAllocator<U> other;
};
};
template<typename T, typename U>
constexpr bool operator==(
const MyAllocator<T> &,
const MyAllocator<U> &) noexcept
{
return true;
}
template<typename T, typename U>
constexpr bool operator!=(
const MyAllocator<T> &,
const MyAllocator<U> &) noexcept
{
return false;
}
template<
typename PublishedMessageAllocatorT,
typename PublisherAllocatorT,
typename SubscribedMessageAllocatorT,
typename SubscriptionAllocatorT,
typename MessageMemoryStrategyAllocatorT,
typename MemoryStrategyAllocatorT,
typename ExpectedExceptionT
>
void
do_custom_allocator_test(
PublishedMessageAllocatorT published_message_allocator,
PublisherAllocatorT publisher_allocator,
SubscribedMessageAllocatorT /* subscribed_message_allocator */, // intentionally unused
SubscriptionAllocatorT subscription_allocator,
MessageMemoryStrategyAllocatorT message_memory_strategy,
MemoryStrategyAllocatorT memory_strategy_allocator)
{
using PublishedMessageAllocTraits =
rclcpp::allocator::AllocRebind<test_msgs::msg::Empty, PublishedMessageAllocatorT>;
using PublishedMessageAlloc = typename PublishedMessageAllocTraits::allocator_type;
using PublishedMessageDeleter =
rclcpp::allocator::Deleter<PublishedMessageAlloc, test_msgs::msg::Empty>;
using SubscribedMessageAllocTraits =
rclcpp::allocator::AllocRebind<test_msgs::msg::Empty, SubscribedMessageAllocatorT>;
using SubscribedMessageAlloc = typename SubscribedMessageAllocTraits::allocator_type;
using SubscribedMessageDeleter =
rclcpp::allocator::Deleter<SubscribedMessageAlloc, test_msgs::msg::Empty>;
// init and node
auto context = std::make_shared<rclcpp::Context>();
context->init(0, nullptr);
auto node = std::make_shared<rclcpp::Node>(
"custom_allocator_test",
rclcpp::NodeOptions().context(context).use_intra_process_comms(true));
// publisher
auto shared_publisher_allocator = std::make_shared<PublisherAllocatorT>(publisher_allocator);
rclcpp::PublisherOptionsWithAllocator<PublisherAllocatorT> publisher_options;
publisher_options.allocator = shared_publisher_allocator;
auto publisher =
node->create_publisher<test_msgs::msg::Empty>("custom_allocator_test", 10, publisher_options);
// callback for subscription
uint32_t counter = 0;
std::promise<std::unique_ptr<test_msgs::msg::Empty, SubscribedMessageDeleter>> received_message;
auto received_message_future = received_message.get_future();
auto callback =
[&counter, &received_message](
std::unique_ptr<test_msgs::msg::Empty, SubscribedMessageDeleter> msg)
{
++counter;
received_message.set_value(std::move(msg));
};
// subscription
auto shared_subscription_allocator =
std::make_shared<SubscriptionAllocatorT>(subscription_allocator);
rclcpp::SubscriptionOptionsWithAllocator<SubscriptionAllocatorT> subscription_options;
subscription_options.allocator = shared_subscription_allocator;
auto shared_message_strategy_allocator =
std::make_shared<MessageMemoryStrategyAllocatorT>(message_memory_strategy);
auto msg_mem_strat = std::make_shared<
rclcpp::message_memory_strategy::MessageMemoryStrategy<
test_msgs::msg::Empty,
MessageMemoryStrategyAllocatorT
>
>(shared_message_strategy_allocator);
using CallbackMessageT =
typename rclcpp::subscription_traits::has_message_type<decltype(callback)>::type;
auto subscriber = node->create_subscription<
test_msgs::msg::Empty,
decltype(callback),
SubscriptionAllocatorT,
rclcpp::Subscription<CallbackMessageT, SubscriptionAllocatorT>,
rclcpp::message_memory_strategy::MessageMemoryStrategy<
CallbackMessageT,
MessageMemoryStrategyAllocatorT
>
>(
"custom_allocator_test",
10,
std::forward<decltype(callback)>(callback),
subscription_options,
msg_mem_strat);
// executor memory strategy
using rclcpp::memory_strategies::allocator_memory_strategy::AllocatorMemoryStrategy;
auto shared_memory_strategy_allocator = std::make_shared<MemoryStrategyAllocatorT>(
memory_strategy_allocator);
std::shared_ptr<rclcpp::memory_strategy::MemoryStrategy> memory_strategy =
std::make_shared<AllocatorMemoryStrategy<MemoryStrategyAllocatorT>>(
shared_memory_strategy_allocator);
// executor
rclcpp::ExecutorOptions options;
options.memory_strategy = memory_strategy;
options.context = context;
rclcpp::executors::SingleThreadedExecutor executor(options);
executor.add_node(node);
// rebind message_allocator to ensure correct type
PublishedMessageDeleter message_deleter;
PublishedMessageAlloc rebound_message_allocator = published_message_allocator;
rclcpp::allocator::set_allocator_for_deleter(&message_deleter, &rebound_message_allocator);
// allocate a message
auto ptr = PublishedMessageAllocTraits::allocate(rebound_message_allocator, 1);
PublishedMessageAllocTraits::construct(rebound_message_allocator, ptr);
std::unique_ptr<test_msgs::msg::Empty, PublishedMessageDeleter> msg(ptr, message_deleter);
// publisher and receive
if constexpr (std::is_same_v<ExpectedExceptionT, void>) {
// no exception expected
EXPECT_NO_THROW(
{
publisher->publish(std::move(msg));
executor.spin_until_future_complete(received_message_future, std::chrono::seconds(10));
});
EXPECT_EQ(ptr, received_message_future.get().get());
EXPECT_EQ(1u, counter);
} else {
// exception expected
EXPECT_THROW(
{
publisher->publish(std::move(msg));
executor.spin_until_future_complete(received_message_future, std::chrono::seconds(10));
}, ExpectedExceptionT);
}
}
/*
This tests the case where a custom allocator is used correctly, i.e. the same
custom allocator on both sides.
*/
TEST(TestIntraProcessManagerWithAllocators, custom_allocator) {
using PublishedMessageAllocatorT = std::allocator<void>;
using PublisherAllocatorT = std::allocator<void>;
using SubscribedMessageAllocatorT = std::allocator<void>;
using SubscriptionAllocatorT = std::allocator<void>;
using MessageMemoryStrategyAllocatorT = std::allocator<void>;
using MemoryStrategyAllocatorT = std::allocator<void>;
auto allocator = std::allocator<void>();
do_custom_allocator_test<
PublishedMessageAllocatorT,
PublisherAllocatorT,
SubscribedMessageAllocatorT,
SubscriptionAllocatorT,
MessageMemoryStrategyAllocatorT,
MemoryStrategyAllocatorT,
void // no exception expected
>(allocator, allocator, allocator, allocator, allocator, allocator);
}
/*
This tests the case where a custom allocator is used incorrectly, i.e. different
custom allocators on both sides.
*/
TEST(TestIntraProcessManagerWithAllocators, custom_allocator_wrong) {
// explicitly use a different allocator here to provoke a failure
using PublishedMessageAllocatorT = std::allocator<void>;
using PublisherAllocatorT = std::allocator<void>;
using SubscribedMessageAllocatorT = MyAllocator<void>;
using SubscriptionAllocatorT = MyAllocator<void>;
using MessageMemoryStrategyAllocatorT = MyAllocator<void>;
using MemoryStrategyAllocatorT = std::allocator<void>;
auto allocator = std::allocator<void>();
auto my_allocator = MyAllocator<void>();
do_custom_allocator_test<
PublishedMessageAllocatorT,
PublisherAllocatorT,
SubscribedMessageAllocatorT,
SubscriptionAllocatorT,
MessageMemoryStrategyAllocatorT,
MemoryStrategyAllocatorT,
std::runtime_error // expected exception
>(allocator, allocator, my_allocator, my_allocator, my_allocator, allocator);
}

View File

@@ -43,7 +43,42 @@ TEST_F(TestLoanedMessage, initialize) {
auto node = std::make_shared<rclcpp::Node>("loaned_message_test_node");
auto pub = node->create_publisher<MessageT>("loaned_message_test_topic", 1);
auto loaned_msg = rclcpp::LoanedMessage<MessageT>(pub.get(), pub->get_allocator());
// suppress deprecated function warning
#if !defined(_WIN32)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#else // !defined(_WIN32)
# pragma warning(push)
# pragma warning(disable: 4996)
#endif
auto pub_allocator = pub->get_allocator();
// remove warning suppression
#if !defined(_WIN32)
# pragma GCC diagnostic pop
#else // !defined(_WIN32)
# pragma warning(pop)
#endif
// suppress deprecated function warning
#if !defined(_WIN32)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#else // !defined(_WIN32)
# pragma warning(push)
# pragma warning(disable: 4996)
#endif
auto loaned_msg = rclcpp::LoanedMessage<MessageT>(pub.get(), pub_allocator);
// remove warning suppression
#if !defined(_WIN32)
# pragma GCC diagnostic pop
#else // !defined(_WIN32)
# pragma warning(pop)
#endif
ASSERT_TRUE(loaned_msg.is_valid());
loaned_msg.get().float32_value = 42.0f;
ASSERT_EQ(42.0f, loaned_msg.get().float32_value);

View File

@@ -84,14 +84,13 @@ TEST_F(TestMemoryStrategy, get_subscription_by_handle) {
rclcpp::SubscriptionBase::SharedPtr found_subscription = nullptr;
{
auto node = std::make_shared<rclcpp::Node>("node", "ns");
auto callback_groups = node->get_node_base_interface()->get_callback_groups();
std::for_each(
callback_groups.begin(), callback_groups.end(),
[&weak_groups_to_nodes, &node](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
node->for_each_callback_group(
[node, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
{
weak_groups_to_nodes.insert(
std::pair<rclcpp::CallbackGroup::WeakPtr,
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
weak_group_ptr,
group_ptr,
node->get_node_base_interface()));
});
memory_strategy()->collect_entities(weak_groups_to_nodes);
@@ -131,14 +130,13 @@ TEST_F(TestMemoryStrategy, get_service_by_handle) {
rclcpp::ServiceBase::SharedPtr found_service = nullptr;
{
auto node = std::make_shared<rclcpp::Node>("node", "ns");
auto callback_groups = node->get_node_base_interface()->get_callback_groups();
std::for_each(
callback_groups.begin(), callback_groups.end(),
[&weak_groups_to_nodes, &node](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
node->for_each_callback_group(
[node, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
{
weak_groups_to_nodes.insert(
std::pair<rclcpp::CallbackGroup::WeakPtr,
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
weak_group_ptr,
group_ptr,
node->get_node_base_interface()));
});
memory_strategy()->collect_entities(weak_groups_to_nodes);
@@ -184,14 +182,13 @@ TEST_F(TestMemoryStrategy, get_client_by_handle) {
rclcpp::ClientBase::SharedPtr found_client = nullptr;
{
auto node = std::make_shared<rclcpp::Node>("node", "ns");
auto callback_groups = node->get_node_base_interface()->get_callback_groups();
std::for_each(
callback_groups.begin(), callback_groups.end(),
[&weak_groups_to_nodes, &node](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
node->for_each_callback_group(
[node, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
{
weak_groups_to_nodes.insert(
std::pair<rclcpp::CallbackGroup::WeakPtr,
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
weak_group_ptr,
group_ptr,
node->get_node_base_interface()));
});
memory_strategy()->collect_entities(weak_groups_to_nodes);
@@ -232,14 +229,13 @@ TEST_F(TestMemoryStrategy, get_timer_by_handle) {
rclcpp::TimerBase::SharedPtr found_timer = nullptr;
{
auto node = std::make_shared<rclcpp::Node>("node", "ns");
auto callback_groups = node->get_node_base_interface()->get_callback_groups();
std::for_each(
callback_groups.begin(), callback_groups.end(),
[&weak_groups_to_nodes, &node](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
node->for_each_callback_group(
[node, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
{
weak_groups_to_nodes.insert(
std::pair<rclcpp::CallbackGroup::WeakPtr,
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
weak_group_ptr,
group_ptr,
node->get_node_base_interface()));
});
memory_strategy()->collect_entities(weak_groups_to_nodes);
@@ -281,14 +277,13 @@ TEST_F(TestMemoryStrategy, get_node_by_group) {
{
auto node = std::make_shared<rclcpp::Node>("node", "ns");
auto node_handle = node->get_node_base_interface();
auto callback_groups = node_handle->get_callback_groups();
std::for_each(
callback_groups.begin(), callback_groups.end(),
[&weak_groups_to_nodes, &node_handle](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
node_handle->for_each_callback_group(
[node_handle, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
{
weak_groups_to_nodes.insert(
std::pair<rclcpp::CallbackGroup::WeakPtr,
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
weak_group_ptr,
group_ptr,
node_handle));
});
memory_strategy()->collect_entities(weak_groups_to_nodes);
@@ -325,14 +320,13 @@ TEST_F(TestMemoryStrategy, get_group_by_subscription) {
rclcpp::CallbackGroup::SharedPtr callback_group = nullptr;
{
auto node = std::make_shared<rclcpp::Node>("node", "ns");
auto callback_groups = node->get_node_base_interface()->get_callback_groups();
std::for_each(
callback_groups.begin(), callback_groups.end(),
[&weak_groups_to_nodes, &node](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
node->for_each_callback_group(
[node, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
{
weak_groups_to_nodes.insert(
std::pair<rclcpp::CallbackGroup::WeakPtr,
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
weak_group_ptr,
group_ptr,
node->get_node_base_interface()));
});
memory_strategy()->collect_entities(weak_groups_to_nodes);
@@ -379,14 +373,13 @@ TEST_F(TestMemoryStrategy, get_group_by_service) {
rclcpp::ServiceBase::SharedPtr service = nullptr;
{
auto node = std::make_shared<rclcpp::Node>("node", "ns");
auto callback_groups = node->get_node_base_interface()->get_callback_groups();
std::for_each(
callback_groups.begin(), callback_groups.end(),
[&weak_groups_to_nodes, &node](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
node->for_each_callback_group(
[node, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
{
weak_groups_to_nodes.insert(
std::pair<rclcpp::CallbackGroup::WeakPtr,
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
weak_group_ptr,
group_ptr,
node->get_node_base_interface()));
});
memory_strategy()->collect_entities(weak_groups_to_nodes);
@@ -424,14 +417,13 @@ TEST_F(TestMemoryStrategy, get_group_by_client) {
rclcpp::ClientBase::SharedPtr client = nullptr;
{
auto node = std::make_shared<rclcpp::Node>("node", "ns");
auto callback_groups = node->get_node_base_interface()->get_callback_groups();
std::for_each(
callback_groups.begin(), callback_groups.end(),
[&weak_groups_to_nodes, &node](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
node->for_each_callback_group(
[node, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
{
weak_groups_to_nodes.insert(
std::pair<rclcpp::CallbackGroup::WeakPtr,
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
weak_group_ptr,
group_ptr,
node->get_node_base_interface()));
});
memory_strategy()->collect_entities(weak_groups_to_nodes);
@@ -464,14 +456,13 @@ TEST_F(TestMemoryStrategy, get_group_by_timer) {
rclcpp::TimerBase::SharedPtr timer = nullptr;
{
auto node = std::make_shared<rclcpp::Node>("node", "ns");
auto callback_groups = node->get_node_base_interface()->get_callback_groups();
std::for_each(
callback_groups.begin(), callback_groups.end(),
[&weak_groups_to_nodes, &node](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
node->for_each_callback_group(
[node, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
{
weak_groups_to_nodes.insert(
std::pair<rclcpp::CallbackGroup::WeakPtr,
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
weak_group_ptr,
group_ptr,
node->get_node_base_interface()));
});
memory_strategy()->collect_entities(weak_groups_to_nodes);
@@ -504,14 +495,13 @@ TEST_F(TestMemoryStrategy, get_group_by_waitable) {
rclcpp::Waitable::SharedPtr waitable = nullptr;
{
auto node = std::make_shared<rclcpp::Node>("node", "ns");
auto callback_groups = node->get_node_base_interface()->get_callback_groups();
std::for_each(
callback_groups.begin(), callback_groups.end(),
[&weak_groups_to_nodes, &node](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
node->for_each_callback_group(
[node, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
{
weak_groups_to_nodes.insert(
std::pair<rclcpp::CallbackGroup::WeakPtr,
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
weak_group_ptr,
group_ptr,
node->get_node_base_interface()));
});
memory_strategy()->collect_entities(weak_groups_to_nodes);

View File

@@ -14,6 +14,9 @@
#include <gtest/gtest.h>
#include <chrono>
#include <functional>
#include <limits>
#include <map>
#include <memory>
#include <string>
@@ -22,10 +25,10 @@
#include "rclcpp/exceptions.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/scope_exit.hpp"
#include "rclcpp/rclcpp.hpp"
#include "rcpputils/filesystem_helper.hpp"
#include "rcpputils/scope_exit.hpp"
#include "rmw/validate_namespace.h"
@@ -99,6 +102,7 @@ TEST_F(TestNode, get_name_and_namespace) {
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
EXPECT_STREQ("my_node", node->get_name());
EXPECT_STREQ("/ns", node->get_namespace());
EXPECT_STREQ("/ns", node->get_effective_namespace().c_str());
EXPECT_STREQ("/ns/my_node", node->get_fully_qualified_name());
}
{
@@ -113,30 +117,35 @@ TEST_F(TestNode, get_name_and_namespace) {
auto node = std::make_shared<rclcpp::Node>("my_node", "ns");
EXPECT_STREQ("my_node", node->get_name());
EXPECT_STREQ("/ns", node->get_namespace());
EXPECT_STREQ("/ns", node->get_effective_namespace().c_str());
EXPECT_STREQ("/ns/my_node", node->get_fully_qualified_name());
}
{
auto node = std::make_shared<rclcpp::Node>("my_node");
EXPECT_STREQ("my_node", node->get_name());
EXPECT_STREQ("/", node->get_namespace());
EXPECT_STREQ("/", node->get_effective_namespace().c_str());
EXPECT_STREQ("/my_node", node->get_fully_qualified_name());
}
{
auto node = std::make_shared<rclcpp::Node>("my_node", "");
EXPECT_STREQ("my_node", node->get_name());
EXPECT_STREQ("/", node->get_namespace());
EXPECT_STREQ("/", node->get_effective_namespace().c_str());
EXPECT_STREQ("/my_node", node->get_fully_qualified_name());
}
{
auto node = std::make_shared<rclcpp::Node>("my_node", "/my/ns");
EXPECT_STREQ("my_node", node->get_name());
EXPECT_STREQ("/my/ns", node->get_namespace());
EXPECT_STREQ("/my/ns", node->get_effective_namespace().c_str());
EXPECT_STREQ("/my/ns/my_node", node->get_fully_qualified_name());
}
{
auto node = std::make_shared<rclcpp::Node>("my_node", "my/ns");
EXPECT_STREQ("my_node", node->get_name());
EXPECT_STREQ("/my/ns", node->get_namespace());
EXPECT_STREQ("/my/ns", node->get_effective_namespace().c_str());
EXPECT_STREQ("/my/ns/my_node", node->get_fully_qualified_name());
}
{
@@ -275,6 +284,13 @@ TEST_F(TestNode, subnode_construction_and_destruction) {
auto subnode = node->create_sub_node("~sub_ns");
}, rclcpp::exceptions::InvalidNamespaceError);
}
{
ASSERT_THROW(
{
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
auto subnode = node->create_sub_node("");
}, rclcpp::exceptions::NameValidationError);
}
}
TEST_F(TestNode, get_logger) {
@@ -333,9 +349,14 @@ TEST_F(TestNode, declare_parameter_with_no_initial_values) {
rcl_interfaces::msg::ParameterDescriptor descriptor;
descriptor.dynamic_typing = true;
// no default, no initial
const std::string parameter_name = "parameter"_unq;
rclcpp::ParameterValue value = node->declare_parameter(
"parameter"_unq, rclcpp::ParameterValue{}, descriptor);
parameter_name, rclcpp::ParameterValue{}, descriptor);
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_NOT_SET);
// Does not throw if unset before access
EXPECT_EQ(
rclcpp::PARAMETER_NOT_SET,
node->get_parameter(parameter_name).get_parameter_value().get_type());
}
{
// int default, no initial
@@ -402,7 +423,8 @@ TEST_F(TestNode, declare_parameter_with_no_initial_values) {
return result;
};
auto handler = node->add_on_set_parameters_callback(on_set_parameters);
RCLCPP_SCOPE_EXIT({node->remove_on_set_parameters_callback(handler.get());}); // always reset
RCPPUTILS_SCOPE_EXIT(
{node->remove_on_set_parameters_callback(handler.get());}); // always reset
EXPECT_THROW(
{node->declare_parameter<std::string>(name, "not an int");},
rclcpp::exceptions::InvalidParameterValueException);
@@ -649,7 +671,8 @@ TEST_F(TestNode, declare_parameter_with_overrides) {
return result;
};
auto handler = node->add_on_set_parameters_callback(on_set_parameters);
RCLCPP_SCOPE_EXIT({node->remove_on_set_parameters_callback(handler.get());}); // always reset
RCPPUTILS_SCOPE_EXIT(
{node->remove_on_set_parameters_callback(handler.get());}); // always reset
EXPECT_THROW(
{node->declare_parameter<int>(name, 43);},
rclcpp::exceptions::InvalidParameterValueException);
@@ -776,7 +799,8 @@ TEST_F(TestNode, declare_parameters_with_no_initial_values) {
return result;
};
auto handler = node->add_on_set_parameters_callback(on_set_parameters);
RCLCPP_SCOPE_EXIT({node->remove_on_set_parameters_callback(handler.get());}); // always reset
RCPPUTILS_SCOPE_EXIT(
{node->remove_on_set_parameters_callback(handler.get());}); // always reset
EXPECT_THROW(
{node->declare_parameters<std::string>("", {{name, "not an int"}});},
rclcpp::exceptions::InvalidParameterValueException);
@@ -1004,7 +1028,8 @@ TEST_F(TestNode, set_parameter_undeclared_parameters_not_allowed) {
return result;
};
auto handler = node->add_on_set_parameters_callback(on_set_parameters);
RCLCPP_SCOPE_EXIT({node->remove_on_set_parameters_callback(handler.get());}); // always reset
RCPPUTILS_SCOPE_EXIT(
{node->remove_on_set_parameters_callback(handler.get());}); // always reset
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name, 43)).successful);
}
@@ -1512,7 +1537,8 @@ TEST_F(TestNode, set_parameters_undeclared_parameters_not_allowed) {
return result;
};
auto handler = node->add_on_set_parameters_callback(on_set_parameters);
RCLCPP_SCOPE_EXIT({node->remove_on_set_parameters_callback(handler.get());}); // always reset
RCPPUTILS_SCOPE_EXIT(
{node->remove_on_set_parameters_callback(handler.get());}); // always reset
auto rets = node->set_parameters(
{
@@ -1704,7 +1730,8 @@ TEST_F(TestNode, set_parameters_atomically_undeclared_parameters_not_allowed) {
return result;
};
auto handler = node->add_on_set_parameters_callback(on_set_parameters);
RCLCPP_SCOPE_EXIT({node->remove_on_set_parameters_callback(handler.get());}); // always reset
RCPPUTILS_SCOPE_EXIT(
{node->remove_on_set_parameters_callback(handler.get());}); // always reset
auto ret = node->set_parameters_atomically(
{
@@ -1830,7 +1857,8 @@ TEST_F(TestNode, set_parameters_atomically_undeclared_parameters_allowed) {
return result;
};
auto handler = node->add_on_set_parameters_callback(on_set_parameters);
RCLCPP_SCOPE_EXIT({node->remove_on_set_parameters_callback(handler.get());}); // always reset
RCPPUTILS_SCOPE_EXIT(
{node->remove_on_set_parameters_callback(handler.get());}); // always reset
auto ret = node->set_parameters_atomically(
{
@@ -2508,6 +2536,59 @@ TEST_F(TestNode, get_parameter_types_undeclared_parameters_allowed) {
}
}
// test declare parameter with int, int64_t, float and double vector
TEST_F(TestNode, declare_parameter_with_vector) {
auto node = std::make_shared<rclcpp::Node>(
"test_declare_parameter_with_vector"_unq,
rclcpp::NodeOptions().allow_undeclared_parameters(true));
{
// declare parameter and then get types to check
auto name1 = "parameter"_unq;
auto name2 = "parameter"_unq;
auto name3 = "parameter"_unq;
auto name4 = "parameter"_unq;
node->declare_parameter(name1, std::vector<int>{});
node->declare_parameter(name2, std::vector<int64_t>{});
node->declare_parameter(name3, std::vector<float>{});
node->declare_parameter(name4, std::vector<double>{});
EXPECT_TRUE(node->has_parameter(name1));
EXPECT_TRUE(node->has_parameter(name2));
EXPECT_TRUE(node->has_parameter(name3));
EXPECT_TRUE(node->has_parameter(name4));
auto results = node->get_parameter_types({name1, name2, name3, name4});
EXPECT_EQ(results.size(), 4u);
EXPECT_EQ(results[0], rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER_ARRAY);
EXPECT_EQ(results[1], rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER_ARRAY);
EXPECT_EQ(results[2], rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE_ARRAY);
EXPECT_EQ(results[3], rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE_ARRAY);
}
{
// declare parameter and then get values to check
auto name1 = "parameter"_unq;
auto name2 = "parameter"_unq;
auto name3 = "parameter"_unq;
auto name4 = "parameter"_unq;
int64_t bigger_than_int = INT64_MAX - 42;
double bigger_than_float = std::numeric_limits<double>::max() - 42;
node->declare_parameter(name1, std::vector<int>{1, 2});
node->declare_parameter(name2, std::vector<int64_t>{3, bigger_than_int});
node->declare_parameter(name3, std::vector<float>{1.5f, 2.8f});
node->declare_parameter(name4, std::vector<double>{3.0, bigger_than_float});
std::vector<rclcpp::Parameter> expected = {
{name1, std::vector<int>{1, 2}},
{name2, std::vector<int64_t>{3, bigger_than_int}},
{name3, std::vector<float>{1.5f, 2.8f}},
{name4, std::vector<double>{3.0, bigger_than_float}},
};
EXPECT_EQ(node->get_parameters({name1, name2, name3, name4}), expected);
}
}
void expect_qos_profile_eq(
const rmw_qos_profile_t & qos1, const rmw_qos_profile_t & qos2, bool is_publisher)
{
@@ -2525,6 +2606,34 @@ void expect_qos_profile_eq(
EXPECT_EQ(qos1.liveliness_lease_duration.nsec, qos2.liveliness_lease_duration.nsec);
}
namespace
{
constexpr std::chrono::nanoseconds DEFAULT_EVENT_TIMEOUT = std::chrono::seconds(3);
constexpr std::chrono::nanoseconds DEFAULT_EVENT_SLEEP_PERIOD = std::chrono::milliseconds(100);
bool wait_for_event(
std::shared_ptr<rclcpp::Node> node,
std::function<bool()> predicate,
std::chrono::nanoseconds timeout = DEFAULT_EVENT_TIMEOUT,
std::chrono::nanoseconds sleep_period = DEFAULT_EVENT_SLEEP_PERIOD)
{
auto start = std::chrono::steady_clock::now();
std::chrono::nanoseconds time_slept(0);
bool predicate_result;
while (!(predicate_result = predicate()) && time_slept < timeout) {
rclcpp::Event::SharedPtr graph_event = node->get_graph_event();
node->wait_for_graph_change(graph_event, sleep_period);
time_slept = std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::steady_clock::now() - start);
}
return predicate_result;
}
} // namespace
// test that calling get_publishers_info_by_topic and get_subscriptions_info_by_topic
TEST_F(TestNode, get_publishers_subscriptions_info_by_topic) {
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
@@ -2556,6 +2665,10 @@ TEST_F(TestNode, get_publishers_subscriptions_info_by_topic) {
};
rclcpp::QoS qos = rclcpp::QoS(qos_initialization, rmw_qos_profile_default);
auto publisher = node->create_publisher<test_msgs::msg::BasicTypes>(topic_name, qos);
// Wait for the underlying RMW implementation to catch up with graph changes
auto topic_is_published =
[&]() {return node->get_publishers_info_by_topic(fq_topic_name).size() > 0u;};
ASSERT_TRUE(wait_for_event(node, topic_is_published));
// List should have one item
auto publisher_list = node->get_publishers_info_by_topic(fq_topic_name);
ASSERT_EQ(publisher_list.size(), (size_t)1);
@@ -2596,7 +2709,10 @@ TEST_F(TestNode, get_publishers_subscriptions_info_by_topic) {
};
auto subscriber =
node->create_subscription<test_msgs::msg::BasicTypes>(topic_name, qos2, callback);
// Wait for the underlying RMW implementation to catch up with graph changes
auto topic_is_subscribed =
[&]() {return node->get_subscriptions_info_by_topic(fq_topic_name).size() > 0u;};
ASSERT_TRUE(wait_for_event(node, topic_is_subscribed));
// Both lists should have one item
publisher_list = node->get_publishers_info_by_topic(fq_topic_name);
auto subscription_list = node->get_subscriptions_info_by_topic(fq_topic_name);
@@ -2637,13 +2753,33 @@ TEST_F(TestNode, get_publishers_subscriptions_info_by_topic) {
TEST_F(TestNode, callback_groups) {
auto node = std::make_shared<rclcpp::Node>("node", "ns");
size_t num_callback_groups_in_basic_node = node->get_callback_groups().size();
size_t num_callback_groups_in_basic_node = 0;
node->for_each_callback_group(
[&num_callback_groups_in_basic_node](rclcpp::CallbackGroup::SharedPtr group)
{
(void)group;
num_callback_groups_in_basic_node++;
});
auto group1 = node->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
EXPECT_EQ(1u + num_callback_groups_in_basic_node, node->get_callback_groups().size());
size_t num_callback_groups = 0;
node->for_each_callback_group(
[&num_callback_groups](rclcpp::CallbackGroup::SharedPtr group)
{
(void)group;
num_callback_groups++;
});
EXPECT_EQ(1u + num_callback_groups_in_basic_node, num_callback_groups);
auto group2 = node->create_callback_group(rclcpp::CallbackGroupType::Reentrant);
EXPECT_EQ(2u + num_callback_groups_in_basic_node, node->get_callback_groups().size());
size_t num_callback_groups2 = 0;
node->for_each_callback_group(
[&num_callback_groups2](rclcpp::CallbackGroup::SharedPtr group)
{
(void)group;
num_callback_groups2++;
});
EXPECT_EQ(2u + num_callback_groups_in_basic_node, num_callback_groups2);
}
// This is tested more thoroughly in node_interfaces/test_node_graph
@@ -2761,9 +2897,20 @@ TEST_F(TestNode, static_and_dynamic_typing) {
EXPECT_EQ("hello!", param);
}
{
auto param = node->declare_parameter("integer_override_not_given", rclcpp::PARAMETER_INTEGER);
EXPECT_EQ(rclcpp::PARAMETER_NOT_SET, param.get_type());
// Throws if not set before access
EXPECT_THROW(
node->declare_parameter("integer_override_not_given", rclcpp::PARAMETER_INTEGER),
rclcpp::exceptions::NoParameterOverrideProvided);
node->get_parameter("integer_override_not_given"),
rclcpp::exceptions::ParameterUninitializedException);
}
{
auto param = node->declare_parameter("integer_set_after_declare", rclcpp::PARAMETER_INTEGER);
EXPECT_EQ(rclcpp::PARAMETER_NOT_SET, param.get_type());
auto result = node->set_parameter(rclcpp::Parameter{"integer_set_after_declare", 44});
ASSERT_TRUE(result.successful) << result.reason;
auto get_param = node->get_parameter("integer_set_after_declare");
EXPECT_EQ(44, get_param.as_int());
}
{
EXPECT_THROW(

View File

@@ -0,0 +1,335 @@
// Copyright 2021 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <gtest/gtest.h>
#include <chrono>
#include <functional>
#include <memory>
#include <string>
#include <thread>
#include <utility>
#include <vector>
#include "rclcpp/exceptions.hpp"
#include "rclcpp/loaned_message.hpp"
#include "rclcpp/rclcpp.hpp"
#include "../mocking_utils/patch.hpp"
#include "../utils/rclcpp_gtest_macros.hpp"
#include "test_msgs/msg/empty.hpp"
#include "rclcpp/msg/string.hpp"
#ifdef RMW_IMPLEMENTATION
# define CLASSNAME_(NAME, SUFFIX) NAME ## __ ## SUFFIX
# define CLASSNAME(NAME, SUFFIX) CLASSNAME_(NAME, SUFFIX)
#else
# define CLASSNAME(NAME, SUFFIX) NAME
#endif
using namespace std::chrono_literals;
static const int g_max_loops = 200;
static const std::chrono::milliseconds g_sleep_per_loop(10);
class TestPublisher : public ::testing::Test
{
public:
static void SetUpTestCase()
{
if (!rclcpp::ok()) {
rclcpp::init(0, nullptr);
}
}
protected:
void initialize(const rclcpp::NodeOptions & node_options = rclcpp::NodeOptions())
{
node = std::make_shared<rclcpp::Node>("my_node", "/ns", node_options);
}
void TearDown()
{
node.reset();
}
rclcpp::Node::SharedPtr node;
};
class CLASSNAME (test_intra_process_within_one_node, RMW_IMPLEMENTATION) : public ::testing::Test
{
public:
static void SetUpTestCase()
{
rclcpp::init(0, nullptr);
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
};
namespace rclcpp
{
template<>
struct TypeAdapter<std::string, rclcpp::msg::String>
{
using is_specialized = std::true_type;
using custom_type = std::string;
using ros_message_type = rclcpp::msg::String;
static void
convert_to_ros_message(
const custom_type & source,
ros_message_type & destination)
{
destination.data = source;
}
static void
convert_to_custom(
const ros_message_type & source,
custom_type & destination)
{
destination = source.data;
}
};
// Throws in conversion
template<>
struct TypeAdapter<int, rclcpp::msg::String>
{
using is_specialized = std::true_type;
using custom_type = int;
using ros_message_type = rclcpp::msg::String;
static void
convert_to_ros_message(
const custom_type & source,
ros_message_type & destination)
{
(void) source;
(void) destination;
throw std::runtime_error("This should happen");
}
static void
convert_to_custom(
const ros_message_type & source,
custom_type & destination)
{
(void) source;
(void) destination;
}
};
} // namespace rclcpp
/*
* Testing publisher creation signatures with a type adapter.
*/
TEST_F(TestPublisher, various_creation_signatures) {
for (auto is_intra_process : {true, false}) {
rclcpp::NodeOptions options;
options.use_intra_process_comms(is_intra_process);
initialize(options);
{
using StringTypeAdapter = rclcpp::TypeAdapter<std::string, rclcpp::msg::String>;
auto publisher = node->create_publisher<StringTypeAdapter>("topic", 42);
(void)publisher;
}
{
using StringTypeAdapter = rclcpp::adapt_type<std::string>::as<rclcpp::msg::String>;
auto publisher = node->create_publisher<StringTypeAdapter>("topic", 42);
(void)publisher;
}
}
}
/*
* Testing that conversion errors are passed up.
*/
TEST_F(TestPublisher, conversion_exception_is_passed_up) {
using BadStringTypeAdapter = rclcpp::TypeAdapter<int, rclcpp::msg::String>;
for (auto is_intra_process : {true, false}) {
rclcpp::NodeOptions options;
options.use_intra_process_comms(is_intra_process);
initialize(options);
auto pub = node->create_publisher<BadStringTypeAdapter>("topic_name", 1);
EXPECT_THROW(pub->publish(1), std::runtime_error);
}
}
/*
* Testing that publisher sends type adapted types and ROS message types with intra proccess communications.
*/
TEST_F(
CLASSNAME(test_intra_process_within_one_node, RMW_IMPLEMENTATION),
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::SharedPtr msg,
const rclcpp::MessageInfo & message_info
) -> void
{
is_received = true;
ASSERT_STREQ(message_data.c_str(), msg->data.c_str());
ASSERT_TRUE(message_info.get_rmw_message_info().from_intra_process);
};
auto node = rclcpp::Node::make_shared(
"test_intra_process",
rclcpp::NodeOptions().use_intra_process_comms(true));
auto pub = node->create_publisher<StringTypeAdapter>(topic_name, 10);
auto sub = node->create_subscription<rclcpp::msg::String>(topic_name, 1, callback);
auto wait_for_message_to_be_received = [&is_received, &node]() {
rclcpp::executors::SingleThreadedExecutor executor;
int i = 0;
executor.add_node(node);
executor.spin_once(std::chrono::milliseconds(0));
while (!is_received && i < g_max_loops) {
printf("spin_node_once() - callback (1) expected - try %d/%d\n", ++i, g_max_loops);
executor.spin_once(g_sleep_per_loop);
}
};
{
{ // std::string passed by reference
is_received = false;
pub->publish(message_data);
ASSERT_FALSE(is_received);
wait_for_message_to_be_received();
ASSERT_TRUE(is_received);
}
{ // unique pointer to std::string
is_received = false;
auto pu_message = std::make_unique<std::string>(message_data);
pub->publish(std::move(pu_message));
ASSERT_FALSE(is_received);
wait_for_message_to_be_received();
ASSERT_TRUE(is_received);
}
{ // ROS message passed by reference
is_received = false;
rclcpp::msg::String msg;
msg.data = message_data;
pub->publish(msg);
ASSERT_FALSE(is_received);
wait_for_message_to_be_received();
ASSERT_TRUE(is_received);
}
{ // unique ptr to ROS message
is_received = false;
auto pu_msg = std::make_unique<rclcpp::msg::String>();
pu_msg->data = message_data;
pub->publish(std::move(pu_msg));
ASSERT_FALSE(is_received);
wait_for_message_to_be_received();
ASSERT_TRUE(is_received);
}
/* TODO(audrow) Enable once loaned messages are supported for intra process communication
{ // loaned ROS message
// is_received = false;
// std::allocator<void> allocator;
// rclcpp::LoanedMessage<rclcpp::msg::String> loaned_msg(*pub, allocator);
// loaned_msg.get().data = message_data;
// pub->publish(std::move(loaned_msg));
// ASSERT_FALSE(is_received);
// wait_for_message_to_be_received();
// ASSERT_TRUE(is_received);
}
*/
}
}
/*
* Testing that publisher sends type adapted types and ROS message types with inter proccess communications.
*/
TEST_F(TestPublisher, check_type_adapted_message_is_sent_and_received) {
using StringTypeAdapter = rclcpp::TypeAdapter<std::string, rclcpp::msg::String>;
initialize();
const std::string message_data = "Message Data";
const std::string topic_name = "topic_name";
auto do_nothing = [](std::shared_ptr<const rclcpp::msg::String>) {FAIL();};
auto pub = node->create_publisher<StringTypeAdapter>(topic_name, 1);
auto sub = node->create_subscription<rclcpp::msg::String>(topic_name, 1, do_nothing);
auto assert_no_message_was_received_yet = [sub]() {
rclcpp::msg::String msg;
rclcpp::MessageInfo msg_info;
EXPECT_FALSE(sub->take(msg, msg_info));
};
auto assert_message_was_received = [sub, message_data]() {
rclcpp::msg::String msg;
rclcpp::MessageInfo msg_info;
bool message_received = false;
auto start = std::chrono::steady_clock::now();
do {
message_received = sub->take(msg, msg_info);
std::this_thread::sleep_for(100ms);
} while (!message_received && std::chrono::steady_clock::now() - start < 10s);
EXPECT_TRUE(message_received);
ASSERT_STREQ(message_data.c_str(), msg.data.c_str());
};
{ // std::string passed by reference
assert_no_message_was_received_yet();
pub->publish(message_data);
assert_message_was_received();
}
{ // unique pointer to std::string
assert_no_message_was_received_yet();
auto pu_message = std::make_unique<std::string>(message_data);
pub->publish(std::move(pu_message));
assert_message_was_received();
}
{ // ROS message passed by reference
assert_no_message_was_received_yet();
rclcpp::msg::String msg;
msg.data = message_data;
pub->publish(msg);
assert_message_was_received();
}
{ // unique ptr to ROS message
assert_no_message_was_received_yet();
auto pu_msg = std::make_unique<rclcpp::msg::String>();
pu_msg->data = message_data;
pub->publish(std::move(pu_msg));
assert_message_was_received();
}
{ // loaned ROS message
assert_no_message_was_received_yet();
std::allocator<void> allocator;
rclcpp::LoanedMessage<rclcpp::msg::String> loaned_msg(*pub, allocator);
loaned_msg.get().data = message_data;
pub->publish(std::move(loaned_msg));
rclcpp::PublisherOptionsWithAllocator<std::allocator<void>> options;
assert_message_was_received();
}
}

View File

@@ -510,7 +510,7 @@ INSTANTIATE_TEST_SUITE_P(
TEST_F(TestSubscription, get_network_flow_endpoints_errors) {
initialize();
const rclcpp::QoS subscription_qos(1);
auto subscription_callback = [](const test_msgs::msg::Empty::SharedPtr msg) {
auto subscription_callback = [](test_msgs::msg::Empty::SharedPtr msg) {
(void)msg;
};
auto subscription = node->create_subscription<test_msgs::msg::Empty>(

View File

@@ -0,0 +1,563 @@
// Copyright 2021 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <gtest/gtest.h>
#include <chrono>
#include <functional>
#include <memory>
#include <string>
#include <thread>
#include <utility>
#include <vector>
#include "rclcpp/exceptions.hpp"
#include "rclcpp/loaned_message.hpp"
#include "rclcpp/rclcpp.hpp"
#include "../mocking_utils/patch.hpp"
#include "../utils/rclcpp_gtest_macros.hpp"
#include "test_msgs/msg/empty.hpp"
#include "rclcpp/msg/string.hpp"
#ifdef RMW_IMPLEMENTATION
# define CLASSNAME_(NAME, SUFFIX) NAME ## __ ## SUFFIX
# define CLASSNAME(NAME, SUFFIX) CLASSNAME_(NAME, SUFFIX)
#else
# define CLASSNAME(NAME, SUFFIX) NAME
#endif
using namespace std::chrono_literals;
static const int g_max_loops = 200;
static const std::chrono::milliseconds g_sleep_per_loop(10);
class TestSubscription : public ::testing::Test
{
public:
static void SetUpTestCase()
{
if (!rclcpp::ok()) {
rclcpp::init(0, nullptr);
}
}
protected:
void initialize(const rclcpp::NodeOptions & node_options = rclcpp::NodeOptions())
{
node = std::make_shared<rclcpp::Node>("my_node", "/ns", node_options);
}
void TearDown()
{
node.reset();
}
rclcpp::Node::SharedPtr node;
};
class CLASSNAME (test_intra_process_within_one_node, RMW_IMPLEMENTATION) : public ::testing::Test
{
public:
static void SetUpTestCase()
{
rclcpp::init(0, nullptr);
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
};
namespace rclcpp
{
template<>
struct TypeAdapter<std::string, rclcpp::msg::String>
{
using is_specialized = std::true_type;
using custom_type = std::string;
using ros_message_type = rclcpp::msg::String;
static void
convert_to_ros_message(
const custom_type & source,
ros_message_type & destination)
{
destination.data = source;
}
static void
convert_to_custom(
const ros_message_type & source,
custom_type & destination)
{
destination = source.data;
}
};
} // namespace rclcpp
void wait_for_message_to_be_received(
bool & is_received,
const std::shared_ptr<rclcpp::Node> & node)
{
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node);
executor.spin_once(std::chrono::milliseconds(0));
int i = 0;
while (!is_received && i < g_max_loops) {
printf("spin_node_once() - callback (1) expected - try %d/%d\n", ++i, g_max_loops);
executor.spin_once(g_sleep_per_loop);
}
}
/*
* Testing publisher creation signatures with a type adapter.
*/
TEST_F(TestSubscription, various_creation_signatures) {
initialize();
{
using StringTypeAdapter = rclcpp::TypeAdapter<std::string, rclcpp::msg::String>;
auto sub =
node->create_subscription<StringTypeAdapter>("topic", 42, [](const std::string &) {});
(void)sub;
}
{
using StringTypeAdapter = rclcpp::adapt_type<std::string>::as<rclcpp::msg::String>;
auto sub =
node->create_subscription<StringTypeAdapter>("topic", 42, [](const std::string &) {});
(void)sub;
}
}
/*
* Testing that subscriber receives type adapted types and ROS message types with intra proccess communications.
*/
TEST_F(
CLASSNAME(test_intra_process_within_one_node, RMW_IMPLEMENTATION),
check_type_adapted_messages_are_received_by_intra_process_subscription) {
using StringTypeAdapter = rclcpp::TypeAdapter<std::string, rclcpp::msg::String>;
const std::string message_data = "Message Data";
const std::string topic_name = "topic_name";
auto node = rclcpp::Node::make_shared(
"test_intra_process",
rclcpp::NodeOptions().use_intra_process_comms(true));
auto pub = node->create_publisher<rclcpp::msg::String>(topic_name, 1);
rclcpp::msg::String msg;
msg.data = message_data;
{
{ // const std::string &
bool is_received = false;
auto callback =
[message_data, &is_received](
const std::string & msg) -> void {
is_received = true;
ASSERT_STREQ(message_data.c_str(), msg.c_str());
};
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
pub->publish(msg);
ASSERT_FALSE(is_received);
wait_for_message_to_be_received(is_received, node);
ASSERT_TRUE(is_received);
}
{ // const std::string & with message info
bool is_received = false;
auto callback =
[message_data, &is_received](
const std::string & msg,
const rclcpp::MessageInfo & message_info) -> void {
is_received = true;
ASSERT_STREQ(message_data.c_str(), msg.c_str());
ASSERT_TRUE(message_info.get_rmw_message_info().from_intra_process);
};
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
pub->publish(msg);
ASSERT_FALSE(is_received);
wait_for_message_to_be_received(is_received, node);
ASSERT_TRUE(is_received);
}
{ // std::shared_ptr<std::string>
bool is_received = false;
auto callback =
[message_data, &is_received](
std::shared_ptr<const std::string> msg) -> void {
is_received = true;
ASSERT_STREQ(message_data.c_str(), (*msg).c_str());
};
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
pub->publish(msg);
ASSERT_FALSE(is_received);
wait_for_message_to_be_received(is_received, node);
ASSERT_TRUE(is_received);
}
{ // std::shared_ptr<const std::string> with message info
bool is_received = false;
auto callback =
[message_data, &is_received](
std::shared_ptr<const std::string> msg,
const rclcpp::MessageInfo & message_info) -> void {
is_received = true;
ASSERT_STREQ(message_data.c_str(), (*msg).c_str());
ASSERT_TRUE(message_info.get_rmw_message_info().from_intra_process);
};
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
pub->publish(msg);
ASSERT_FALSE(is_received);
wait_for_message_to_be_received(is_received, node);
ASSERT_TRUE(is_received);
}
{ // std::unique_ptr<std::string>
bool is_received = false;
auto callback =
[message_data, &is_received](
std::unique_ptr<std::string> msg) -> void {
is_received = true;
ASSERT_STREQ(message_data.c_str(), (*msg).c_str());
};
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
pub->publish(msg);
ASSERT_FALSE(is_received);
wait_for_message_to_be_received(is_received, node);
ASSERT_TRUE(is_received);
}
{ // std::unique_ptr<std::string> with message info
bool is_received = false;
auto callback =
[message_data, &is_received](
std::unique_ptr<std::string> msg,
const rclcpp::MessageInfo & message_info) -> void {
is_received = true;
ASSERT_STREQ(message_data.c_str(), (*msg).c_str());
ASSERT_TRUE(message_info.get_rmw_message_info().from_intra_process);
};
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
pub->publish(msg);
ASSERT_FALSE(is_received);
wait_for_message_to_be_received(is_received, node);
ASSERT_TRUE(is_received);
}
{ // const rclcpp::msg::String &
bool is_received = false;
auto callback =
[message_data, &is_received](
const rclcpp::msg::String & msg) -> void {
is_received = true;
ASSERT_STREQ(message_data.c_str(), msg.data.c_str());
};
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
pub->publish(msg);
ASSERT_FALSE(is_received);
wait_for_message_to_be_received(is_received, node);
ASSERT_TRUE(is_received);
}
{ // const rclcpp::msg::String & with message info
bool is_received = false;
auto callback =
[message_data, &is_received](
const rclcpp::msg::String & 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 sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
pub->publish(msg);
ASSERT_FALSE(is_received);
wait_for_message_to_be_received(is_received, node);
ASSERT_TRUE(is_received);
}
{ // std::shared_ptr<rclcpp::msg::String>
bool is_received = false;
auto callback =
[message_data, &is_received](
std::shared_ptr<const rclcpp::msg::String> msg) -> void {
is_received = true;
ASSERT_STREQ(message_data.c_str(), msg->data.c_str());
};
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
pub->publish(msg);
ASSERT_FALSE(is_received);
wait_for_message_to_be_received(is_received, node);
ASSERT_TRUE(is_received);
}
{ // std::shared_ptr<rclcpp::msg::String> with message info
bool is_received = false;
auto callback =
[message_data, &is_received](
std::shared_ptr<const rclcpp::msg::String> 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 sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
pub->publish(msg);
ASSERT_FALSE(is_received);
wait_for_message_to_be_received(is_received, node);
ASSERT_TRUE(is_received);
}
{ // std::unique_ptr<rclcpp::msg::String>
bool is_received = false;
auto callback =
[message_data, &is_received](
std::unique_ptr<rclcpp::msg::String> msg) -> void {
is_received = true;
ASSERT_STREQ(message_data.c_str(), msg->data.c_str());
};
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
pub->publish(msg);
ASSERT_FALSE(is_received);
wait_for_message_to_be_received(is_received, node);
ASSERT_TRUE(is_received);
}
{ // std::unique_ptr<rclcpp::msg::String> with message info
bool is_received = false;
auto callback =
[message_data, &is_received](
std::unique_ptr<rclcpp::msg::String> 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 sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
pub->publish(msg);
ASSERT_FALSE(is_received);
wait_for_message_to_be_received(is_received, node);
ASSERT_TRUE(is_received);
}
}
}
/*
* Testing that subscriber receives type adapted types and ROS message types with inter proccess communications.
*/
TEST_F(
CLASSNAME(test_intra_process_within_one_node, RMW_IMPLEMENTATION),
check_type_adapted_messages_are_received_by_inter_process_subscription) {
using StringTypeAdapter = rclcpp::TypeAdapter<std::string, rclcpp::msg::String>;
const std::string message_data = "Message Data";
const std::string topic_name = "topic_name";
auto node = rclcpp::Node::make_shared(
"test_intra_process",
rclcpp::NodeOptions().use_intra_process_comms(false));
auto pub = node->create_publisher<rclcpp::msg::String>(topic_name, 1);
rclcpp::msg::String msg;
msg.data = message_data;
{
{ // const std::string &
bool is_received = false;
auto callback =
[message_data, &is_received](const std::string & msg) -> void {
is_received = true;
ASSERT_STREQ(message_data.c_str(), msg.c_str());
};
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
pub->publish(msg);
ASSERT_FALSE(is_received);
wait_for_message_to_be_received(is_received, node);
ASSERT_TRUE(is_received);
}
{ // const std::string & with message info
bool is_received = false;
auto callback =
[message_data, &is_received](
const std::string & msg, const rclcpp::MessageInfo & message_info) -> void
{
is_received = true;
ASSERT_STREQ(message_data.c_str(), msg.c_str());
ASSERT_FALSE(message_info.get_rmw_message_info().from_intra_process);
};
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
pub->publish(msg);
ASSERT_FALSE(is_received);
wait_for_message_to_be_received(is_received, node);
ASSERT_TRUE(is_received);
}
{ // std::shared_ptr<std::string>
bool is_received = false;
auto callback =
[message_data, &is_received](
std::shared_ptr<const std::string> msg) -> void {
is_received = true;
ASSERT_STREQ(message_data.c_str(), (*msg).c_str());
};
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
pub->publish(msg);
ASSERT_FALSE(is_received);
wait_for_message_to_be_received(is_received, node);
ASSERT_TRUE(is_received);
}
{ // std::shared_ptr<const std::string> with message info
bool is_received = false;
auto callback =
[message_data, &is_received](
std::shared_ptr<const std::string> msg,
const rclcpp::MessageInfo & message_info) -> void {
is_received = true;
ASSERT_STREQ(message_data.c_str(), (*msg).c_str());
ASSERT_FALSE(message_info.get_rmw_message_info().from_intra_process);
};
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
pub->publish(msg);
ASSERT_FALSE(is_received);
wait_for_message_to_be_received(is_received, node);
ASSERT_TRUE(is_received);
}
{ // std::unique_ptr<std::string>
bool is_received = false;
auto callback =
[message_data, &is_received](
std::unique_ptr<std::string> msg) -> void {
is_received = true;
ASSERT_STREQ(message_data.c_str(), (*msg).c_str());
};
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
pub->publish(msg);
ASSERT_FALSE(is_received);
wait_for_message_to_be_received(is_received, node);
ASSERT_TRUE(is_received);
}
{ // std::unique_ptr<std::string> with message info
bool is_received = false;
auto callback =
[message_data, &is_received](
std::unique_ptr<std::string> msg,
const rclcpp::MessageInfo & message_info) -> void {
is_received = true;
ASSERT_STREQ(message_data.c_str(), (*msg).c_str());
ASSERT_FALSE(message_info.get_rmw_message_info().from_intra_process);
};
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
pub->publish(msg);
ASSERT_FALSE(is_received);
wait_for_message_to_be_received(is_received, node);
ASSERT_TRUE(is_received);
}
{ // const rclcpp::msg::String &
bool is_received = false;
auto callback =
[message_data, &is_received](
const rclcpp::msg::String & msg) -> void {
is_received = true;
ASSERT_STREQ(message_data.c_str(), msg.data.c_str());
};
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
pub->publish(msg);
ASSERT_FALSE(is_received);
wait_for_message_to_be_received(is_received, node);
ASSERT_TRUE(is_received);
}
{ // const rclcpp::msg::String & with message info
bool is_received = false;
auto callback =
[message_data, &is_received](
const rclcpp::msg::String & msg,
const rclcpp::MessageInfo & message_info) -> void {
is_received = true;
ASSERT_STREQ(message_data.c_str(), msg.data.c_str());
ASSERT_FALSE(message_info.get_rmw_message_info().from_intra_process);
};
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
pub->publish(msg);
ASSERT_FALSE(is_received);
wait_for_message_to_be_received(is_received, node);
ASSERT_TRUE(is_received);
}
{ // std::shared_ptr<rclcpp::msg::String>
bool is_received = false;
auto callback =
[message_data, &is_received](
std::shared_ptr<const rclcpp::msg::String> msg) -> void {
is_received = true;
ASSERT_STREQ(message_data.c_str(), msg->data.c_str());
};
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
pub->publish(msg);
ASSERT_FALSE(is_received);
wait_for_message_to_be_received(is_received, node);
ASSERT_TRUE(is_received);
}
{ // std::shared_ptr<rclcpp::msg::String> with message info
bool is_received = false;
auto callback =
[message_data, &is_received](
std::shared_ptr<const rclcpp::msg::String> msg,
const rclcpp::MessageInfo & message_info) -> void {
is_received = true;
ASSERT_STREQ(message_data.c_str(), msg->data.c_str());
ASSERT_FALSE(message_info.get_rmw_message_info().from_intra_process);
};
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
pub->publish(msg);
ASSERT_FALSE(is_received);
wait_for_message_to_be_received(is_received, node);
ASSERT_TRUE(is_received);
}
{ // std::unique_ptr<rclcpp::msg::String>
bool is_received = false;
auto callback =
[message_data, &is_received](
std::unique_ptr<rclcpp::msg::String> msg) -> void {
is_received = true;
ASSERT_STREQ(message_data.c_str(), msg->data.c_str());
};
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
pub->publish(msg);
ASSERT_FALSE(is_received);
wait_for_message_to_be_received(is_received, node);
ASSERT_TRUE(is_received);
}
{ // std::unique_ptr<rclcpp::msg::String> with message info
bool is_received = false;
auto callback =
[message_data, &is_received](
std::unique_ptr<rclcpp::msg::String> msg,
const rclcpp::MessageInfo & message_info) -> void {
is_received = true;
ASSERT_STREQ(message_data.c_str(), msg->data.c_str());
ASSERT_FALSE(message_info.get_rmw_message_info().from_intra_process);
};
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
pub->publish(msg);
ASSERT_FALSE(is_received);
wait_for_message_to_be_received(is_received, node);
ASSERT_TRUE(is_received);
}
}
}

View File

@@ -24,7 +24,6 @@
#include "rclcpp/contexts/default_context.hpp"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/utilities.hpp"
#include "rclcpp/scope_exit.hpp"
#include "../mocking_utils/patch.hpp"
#include "../utils/rclcpp_gtest_macros.hpp"

View File

@@ -0,0 +1,74 @@
// Copyright 2021 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <gtest/gtest.h>
#include <memory>
#include <string>
#include <vector>
#include "rclcpp/node.hpp"
#include "rclcpp/wait_for_message.hpp"
#include "test_msgs/msg/strings.hpp"
#include "test_msgs/message_fixtures.hpp"
using namespace std::chrono_literals;
TEST(TestUtilities, wait_for_message) {
rclcpp::init(0, nullptr);
auto node = std::make_shared<rclcpp::Node>("wait_for_message_node");
using MsgT = test_msgs::msg::Strings;
auto pub = node->create_publisher<MsgT>("wait_for_message_topic", 10);
MsgT out;
auto received = false;
auto wait = std::async(
[&]() {
auto ret = rclcpp::wait_for_message(out, node, "wait_for_message_topic", 5s);
EXPECT_TRUE(ret);
received = true;
});
for (auto i = 0u; i < 10 && received == false; ++i) {
pub->publish(*get_messages_strings()[0]);
std::this_thread::sleep_for(1s);
}
ASSERT_TRUE(received);
EXPECT_EQ(out, *get_messages_strings()[0]);
rclcpp::shutdown();
}
TEST(TestUtilities, wait_for_message_indefinitely) {
rclcpp::init(0, nullptr);
auto node = std::make_shared<rclcpp::Node>("wait_for_message_node2");
using MsgT = test_msgs::msg::Strings;
MsgT out;
auto received = false;
auto wait = std::async(
[&]() {
auto ret = rclcpp::wait_for_message(out, node, "wait_for_message_topic" /*, -1 */);
EXPECT_TRUE(ret);
received = true;
});
rclcpp::shutdown();
ASSERT_FALSE(received);
}

View File

@@ -3,6 +3,33 @@ Changelog for package rclcpp_action
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
13.0.0 (2021-08-23)
-------------------
* Use rcpputils/scope_exit.hpp and remove rclcpp/scope_exit.hpp. (`#1727 <https://github.com/ros2/rclcpp/issues/1727>`_)
* Contributors: Christophe Bedard
12.0.0 (2021-07-26)
-------------------
11.2.0 (2021-07-21)
-------------------
11.1.0 (2021-07-13)
-------------------
* Fixed occasionally missing goal result caused by race condition (`#1677 <https://github.com/ros2/rclcpp/issues/1677>`_)
* Contributors: Kaven Yau
11.0.0 (2021-05-18)
-------------------
* Bump the benchmark timeout for benchmark_action_client (`#1671 <https://github.com/ros2/rclcpp/issues/1671>`_)
* Contributors: Scott K Logan
10.0.0 (2021-05-11)
-------------------
* Returns CancelResponse::REJECT while goal handle failed to transit to CANCELING state (`#1641 <https://github.com/ros2/rclcpp/issues/1641>`_)
* Fix action server deadlock issue that caused by other mutexes locked in CancelCallback (`#1635 <https://github.com/ros2/rclcpp/issues/1635>`_)
* Contributors: Kaven Yau
9.0.2 (2021-04-14)
------------------

View File

@@ -6,6 +6,7 @@ find_package(ament_cmake_ros REQUIRED)
find_package(action_msgs REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rcl_action REQUIRED)
find_package(rcpputils REQUIRED)
find_package(rosidl_runtime_c REQUIRED)
# Default to C++14
@@ -39,6 +40,7 @@ ament_target_dependencies(${PROJECT_NAME}
"action_msgs"
"rcl_action"
"rclcpp"
"rcpputils"
"rosidl_runtime_c"
)
@@ -94,6 +96,7 @@ if(BUILD_TESTING)
ament_add_gtest(test_server test/test_server.cpp TIMEOUT 180)
if(TARGET test_server)
ament_target_dependencies(test_server
"rcpputils"
"rcutils"
"test_msgs"
)

View File

@@ -356,15 +356,26 @@ protected:
CancelResponse
call_handle_cancel_callback(const GoalUUID & uuid) override
{
std::lock_guard<std::mutex> lock(goal_handles_mutex_);
std::shared_ptr<ServerGoalHandle<ActionT>> goal_handle;
{
std::lock_guard<std::mutex> lock(goal_handles_mutex_);
auto element = goal_handles_.find(uuid);
if (element != goal_handles_.end()) {
goal_handle = element->second.lock();
}
}
CancelResponse resp = CancelResponse::REJECT;
auto element = goal_handles_.find(uuid);
if (element != goal_handles_.end()) {
std::shared_ptr<ServerGoalHandle<ActionT>> goal_handle = element->second.lock();
if (goal_handle) {
resp = handle_cancel_(goal_handle);
if (CancelResponse::ACCEPT == resp) {
if (goal_handle) {
resp = handle_cancel_(goal_handle);
if (CancelResponse::ACCEPT == resp) {
try {
goal_handle->_cancel_goal();
} catch (const rclcpp::exceptions::RCLError & ex) {
RCLCPP_DEBUG(
rclcpp::get_logger("rclcpp_action"),
"Failed to cancel goal in call_handle_cancel_callback: %s", ex.what());
return CancelResponse::REJECT;
}
}
}

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>9.0.2</version>
<version>13.0.0</version>
<description>Adds action APIs for C++.</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
<maintainer email="mabel@openrobotics.org">Mabel Zhang</maintainer>
@@ -19,6 +19,7 @@
<depend>action_msgs</depend>
<depend>rclcpp</depend>
<depend>rcl_action</depend>
<depend>rcpputils</depend>
<depend>ament_cmake</depend>

View File

@@ -15,10 +15,11 @@
#include <rcl_action/action_server.h>
#include <rcl_action/wait.h>
#include <rcpputils/scope_exit.hpp>
#include <action_msgs/msg/goal_status_array.hpp>
#include <action_msgs/srv/cancel_goal.hpp>
#include <rclcpp/exceptions.hpp>
#include <rclcpp/scope_exit.hpp>
#include <rclcpp_action/server.hpp>
#include <memory>
@@ -416,7 +417,7 @@ ServerBase::execute_cancel_request_received(std::shared_ptr<void> & data)
rclcpp::exceptions::throw_from_rcl_error(ret);
}
RCLCPP_SCOPE_EXIT(
RCPPUTILS_SCOPE_EXIT(
{
ret = rcl_action_cancel_response_fini(&cancel_response);
if (RCL_RET_OK != ret) {
@@ -500,9 +501,13 @@ ServerBase::execute_result_request_received(std::shared_ptr<void> & data)
result_response = create_result_response(action_msgs::msg::GoalStatus::STATUS_UNKNOWN);
} else {
// Goal exists, check if a result is already available
std::lock_guard<std::recursive_mutex> lock(pimpl_->unordered_map_mutex_);
auto iter = pimpl_->goal_results_.find(uuid);
if (iter != pimpl_->goal_results_.end()) {
result_response = iter->second;
} else {
// Store the request so it can be responded to later
pimpl_->result_requests_[uuid].push_back(request_header);
}
}
@@ -514,10 +519,6 @@ ServerBase::execute_result_request_received(std::shared_ptr<void> & data)
if (RCL_RET_OK != rcl_ret) {
rclcpp::exceptions::throw_from_rcl_error(rcl_ret);
}
} else {
// Store the request so it can be responded to later
std::lock_guard<std::recursive_mutex> lock(pimpl_->unordered_map_mutex_);
pimpl_->result_requests_[uuid].push_back(request_header);
}
data.reset();
}
@@ -581,7 +582,7 @@ ServerBase::publish_status()
rclcpp::exceptions::throw_from_rcl_error(ret);
}
RCLCPP_SCOPE_EXIT(
RCPPUTILS_SCOPE_EXIT(
{
ret = rcl_action_goal_status_array_fini(&c_status_array);
if (RCL_RET_OK != ret) {
@@ -627,19 +628,30 @@ ServerBase::publish_result(const GoalUUID & uuid, std::shared_ptr<void> result_m
}
{
std::lock_guard<std::recursive_mutex> lock(pimpl_->unordered_map_mutex_);
/**
* NOTE: There is a potential deadlock issue if both unordered_map_mutex_ and
* action_server_reentrant_mutex_ locked in other block scopes. Unless using
* std::scoped_lock, locking order must be consistent with the current.
*
* Current locking order:
*
* 1. unordered_map_mutex_
* 2. action_server_reentrant_mutex_
*
*/
std::lock_guard<std::recursive_mutex> unordered_map_lock(pimpl_->unordered_map_mutex_);
pimpl_->goal_results_[uuid] = result_msg;
}
// if there are clients who already asked for the result, send it to them
auto iter = pimpl_->result_requests_.find(uuid);
if (iter != pimpl_->result_requests_.end()) {
for (auto & request_header : iter->second) {
// if there are clients who already asked for the result, send it to them
auto iter = pimpl_->result_requests_.find(uuid);
if (iter != pimpl_->result_requests_.end()) {
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
rcl_ret_t ret = rcl_action_send_result_response(
pimpl_->action_server_.get(), &request_header, result_msg.get());
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret);
for (auto & request_header : iter->second) {
rcl_ret_t ret = rcl_action_send_result_response(
pimpl_->action_server_.get(), &request_header, result_msg.get());
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret);
}
}
}
}

View File

@@ -7,7 +7,7 @@ find_package(performance_test_fixture REQUIRED)
add_performance_test(
benchmark_action_client
benchmark_action_client.cpp
TIMEOUT 120)
TIMEOUT 240)
if(TARGET benchmark_action_client)
target_link_libraries(benchmark_action_client ${PROJECT_NAME})
ament_target_dependencies(benchmark_action_client rclcpp test_msgs)

View File

@@ -15,7 +15,7 @@
#include <rclcpp/exceptions.hpp>
#include <rclcpp/node.hpp>
#include <rclcpp/rclcpp.hpp>
#include <rclcpp/scope_exit.hpp>
#include <rcpputils/scope_exit.hpp>
#include <test_msgs/action/fibonacci.hpp>
#include <gtest/gtest.h>
@@ -1082,7 +1082,7 @@ TEST_F(TestGoalRequestServer, is_ready_rcl_error) {
ASSERT_EQ(
RCL_RET_OK,
rcl_wait_set_init(&wait_set, 10, 10, 10, 10, 10, 10, rcl_context, allocator));
RCLCPP_SCOPE_EXIT(
RCPPUTILS_SCOPE_EXIT(
{
EXPECT_EQ(RCL_RET_OK, rcl_wait_set_fini(&wait_set));
});
@@ -1306,3 +1306,15 @@ TEST_F(TestDeadlockServer, deadlock_while_canceled)
send_goal_request(node_, uuid2_); // deadlock here
t.join();
}
TEST_F(TestDeadlockServer, deadlock_while_succeed_and_canceled)
{
send_goal_request(node_, uuid1_);
std::thread t(&TestDeadlockServer::GoalSucceeded, this);
rclcpp::sleep_for(std::chrono::milliseconds(50));
auto response_ptr = send_cancel_request(node_, uuid1_);
// current goal handle is not cancelable, so it returns ERROR_REJECTED
EXPECT_EQ(CancelResponse::ERROR_REJECTED, response_ptr->return_code);
t.join();
}

View File

@@ -2,6 +2,30 @@
Changelog for package rclcpp_components
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
13.0.0 (2021-08-23)
-------------------
* Update client API to be able to remove pending requests. (`#1734 <https://github.com/ros2/rclcpp/issues/1734>`_)
* Contributors: Ivan Santiago Paunovic
12.0.0 (2021-07-26)
-------------------
11.2.0 (2021-07-21)
-------------------
* Deprecate method names that use CamelCase in rclcpp_components. (`#1716 <https://github.com/ros2/rclcpp/issues/1716>`_)
* Contributors: Rebecca Butler
11.1.0 (2021-07-13)
-------------------
* Added a hook to generate node options in ComponentManager (`#1702 <https://github.com/ros2/rclcpp/issues/1702>`_)
* Contributors: Rebecca Butler
11.0.0 (2021-05-18)
-------------------
10.0.0 (2021-05-11)
-------------------
9.0.2 (2021-04-14)
------------------

View File

@@ -109,7 +109,7 @@ public:
virtual ~ComponentManager();
/// Return a list of valid loadable components in a given package.
/*
/**
* \param package_name name of the package
* \param resource_index name of the executable
* \throws ComponentManagerException if the resource was not found or a invalid resource entry
@@ -122,7 +122,7 @@ public:
const std::string & resource_index = "rclcpp_components") const;
/// Instantiate a component from a dynamic library.
/*
/**
* \param resource a component resource (class name + library path)
* \return a NodeFactory interface
*/
@@ -131,8 +131,17 @@ public:
create_component_factory(const ComponentResource & resource);
protected:
/// Create node options for loaded component
/**
* \param request information with the node to load
* \return node options
*/
RCLCPP_COMPONENTS_PUBLIC
virtual rclcpp::NodeOptions
create_node_options(const std::shared_ptr<LoadNode::Request> request);
/// Service callback to load a new node in the component
/*
/**
* This function allows to add parameters, remap rules, a specific node, name a namespace
* and/or additional arguments.
*
@@ -146,13 +155,27 @@ protected:
*/
RCLCPP_COMPONENTS_PUBLIC
virtual void
OnLoadNode(
on_load_node(
const std::shared_ptr<rmw_request_id_t> request_header,
const std::shared_ptr<LoadNode::Request> request,
std::shared_ptr<LoadNode::Response> response);
/**
* \deprecated Use on_load_node() instead
*/
[[deprecated("Use on_load_node() instead")]]
RCLCPP_COMPONENTS_PUBLIC
virtual void
OnLoadNode(
const std::shared_ptr<rmw_request_id_t> request_header,
const std::shared_ptr<LoadNode::Request> request,
std::shared_ptr<LoadNode::Response> response)
{
on_load_node(request_header, request, response);
}
/// Service callback to unload a node in the component
/*
/**
* \param request_header unused
* \param request unique identifier to remove from the component
* \param response true on the success field if the node unload was succefully, otherwise false
@@ -160,13 +183,27 @@ protected:
*/
RCLCPP_COMPONENTS_PUBLIC
virtual void
OnUnloadNode(
on_unload_node(
const std::shared_ptr<rmw_request_id_t> request_header,
const std::shared_ptr<UnloadNode::Request> request,
std::shared_ptr<UnloadNode::Response> response);
/**
* \deprecated Use on_unload_node() instead
*/
[[deprecated("Use on_unload_node() instead")]]
RCLCPP_COMPONENTS_PUBLIC
virtual void
OnUnloadNode(
const std::shared_ptr<rmw_request_id_t> request_header,
const std::shared_ptr<UnloadNode::Request> request,
std::shared_ptr<UnloadNode::Response> response)
{
on_unload_node(request_header, request, response);
}
/// Service callback to get the list of nodes in the component
/*
/**
* Return a two list: one with the unique identifiers and other with full name of the nodes.
*
* \param request_header unused
@@ -175,11 +212,25 @@ protected:
*/
RCLCPP_COMPONENTS_PUBLIC
virtual void
OnListNodes(
on_list_nodes(
const std::shared_ptr<rmw_request_id_t> request_header,
const std::shared_ptr<ListNodes::Request> request,
std::shared_ptr<ListNodes::Response> response);
/**
* \deprecated Use on_list_nodes() instead
*/
[[deprecated("Use on_list_nodes() instead")]]
RCLCPP_COMPONENTS_PUBLIC
virtual void
OnListNodes(
const std::shared_ptr<rmw_request_id_t> request_header,
const std::shared_ptr<ListNodes::Request> request,
std::shared_ptr<ListNodes::Response> response)
{
on_list_nodes(request_header, request, response);
}
private:
std::weak_ptr<rclcpp::Executor> executor_;

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>9.0.2</version>
<version>13.0.0</version>
<description>Package containing tools for dynamically loadable components</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
<maintainer email="mabel@openrobotics.org">Mabel Zhang</maintainer>

View File

@@ -39,13 +39,13 @@ ComponentManager::ComponentManager(
{
loadNode_srv_ = create_service<LoadNode>(
"~/_container/load_node",
std::bind(&ComponentManager::OnLoadNode, this, _1, _2, _3));
std::bind(&ComponentManager::on_load_node, this, _1, _2, _3));
unloadNode_srv_ = create_service<UnloadNode>(
"~/_container/unload_node",
std::bind(&ComponentManager::OnUnloadNode, this, _1, _2, _3));
std::bind(&ComponentManager::on_unload_node, this, _1, _2, _3));
listNodes_srv_ = create_service<ListNodes>(
"~/_container/list_nodes",
std::bind(&ComponentManager::OnListNodes, this, _1, _2, _3));
std::bind(&ComponentManager::on_list_nodes, this, _1, _2, _3));
}
ComponentManager::~ComponentManager()
@@ -121,8 +121,53 @@ ComponentManager::create_component_factory(const ComponentResource & resource)
return {};
}
rclcpp::NodeOptions
ComponentManager::create_node_options(const std::shared_ptr<LoadNode::Request> request)
{
std::vector<rclcpp::Parameter> parameters;
for (const auto & p : request->parameters) {
parameters.push_back(rclcpp::Parameter::from_parameter_msg(p));
}
std::vector<std::string> remap_rules;
remap_rules.reserve(request->remap_rules.size() * 2 + 1);
remap_rules.push_back("--ros-args");
for (const std::string & rule : request->remap_rules) {
remap_rules.push_back("-r");
remap_rules.push_back(rule);
}
if (!request->node_name.empty()) {
remap_rules.push_back("-r");
remap_rules.push_back("__node:=" + request->node_name);
}
if (!request->node_namespace.empty()) {
remap_rules.push_back("-r");
remap_rules.push_back("__ns:=" + request->node_namespace);
}
auto options = rclcpp::NodeOptions()
.use_global_arguments(false)
.parameter_overrides(parameters)
.arguments(remap_rules);
for (const auto & a : request->extra_arguments) {
const rclcpp::Parameter extra_argument = rclcpp::Parameter::from_parameter_msg(a);
if (extra_argument.get_name() == "use_intra_process_comms") {
if (extra_argument.get_type() != rclcpp::ParameterType::PARAMETER_BOOL) {
throw ComponentManagerException(
"Extra component argument 'use_intra_process_comms' must be a boolean");
}
options.use_intra_process_comms(extra_argument.get_value<bool>());
}
}
return options;
}
void
ComponentManager::OnLoadNode(
ComponentManager::on_load_node(
const std::shared_ptr<rmw_request_id_t> request_header,
const std::shared_ptr<LoadNode::Request> request,
std::shared_ptr<LoadNode::Response> response)
@@ -142,45 +187,7 @@ ComponentManager::OnLoadNode(
continue;
}
std::vector<rclcpp::Parameter> parameters;
for (const auto & p : request->parameters) {
parameters.push_back(rclcpp::Parameter::from_parameter_msg(p));
}
std::vector<std::string> remap_rules;
remap_rules.reserve(request->remap_rules.size() * 2 + 1);
remap_rules.push_back("--ros-args");
for (const std::string & rule : request->remap_rules) {
remap_rules.push_back("-r");
remap_rules.push_back(rule);
}
if (!request->node_name.empty()) {
remap_rules.push_back("-r");
remap_rules.push_back("__node:=" + request->node_name);
}
if (!request->node_namespace.empty()) {
remap_rules.push_back("-r");
remap_rules.push_back("__ns:=" + request->node_namespace);
}
auto options = rclcpp::NodeOptions()
.use_global_arguments(false)
.parameter_overrides(parameters)
.arguments(remap_rules);
for (const auto & a : request->extra_arguments) {
const rclcpp::Parameter extra_argument = rclcpp::Parameter::from_parameter_msg(a);
if (extra_argument.get_name() == "use_intra_process_comms") {
if (extra_argument.get_type() != rclcpp::ParameterType::PARAMETER_BOOL) {
throw ComponentManagerException(
"Extra component argument 'use_intra_process_comms' must be a boolean");
}
options.use_intra_process_comms(extra_argument.get_value<bool>());
}
}
auto options = create_node_options(request);
auto node_id = unique_id_++;
if (0 == node_id) {
@@ -230,7 +237,7 @@ ComponentManager::OnLoadNode(
}
void
ComponentManager::OnUnloadNode(
ComponentManager::on_unload_node(
const std::shared_ptr<rmw_request_id_t> request_header,
const std::shared_ptr<UnloadNode::Request> request,
std::shared_ptr<UnloadNode::Response> response)
@@ -255,7 +262,7 @@ ComponentManager::OnUnloadNode(
}
void
ComponentManager::OnListNodes(
ComponentManager::on_list_nodes(
const std::shared_ptr<rmw_request_id_t> request_header,
const std::shared_ptr<ListNodes::Request> request,
std::shared_ptr<ListNodes::Response> response)

View File

@@ -57,13 +57,14 @@ TEST_F(TestComponentManager, components_api)
request->package_name = "rclcpp_components";
request->plugin_name = "test_rclcpp_components::TestComponentFoo";
auto result = composition_client->async_send_request(request);
auto ret = exec->spin_until_future_complete(result, 5s); // Wait for the result.
auto future = composition_client->async_send_request(request);
auto ret = exec->spin_until_future_complete(future, 5s); // Wait for the result.
auto result = future.get();
EXPECT_EQ(ret, rclcpp::FutureReturnCode::SUCCESS);
EXPECT_EQ(result.get()->success, true);
EXPECT_EQ(result.get()->error_message, "");
EXPECT_EQ(result.get()->full_node_name, "/test_component_foo");
EXPECT_EQ(result.get()->unique_id, 1u);
EXPECT_EQ(result->success, true);
EXPECT_EQ(result->error_message, "");
EXPECT_EQ(result->full_node_name, "/test_component_foo");
EXPECT_EQ(result->unique_id, 1u);
}
{
@@ -71,13 +72,14 @@ TEST_F(TestComponentManager, components_api)
request->package_name = "rclcpp_components";
request->plugin_name = "test_rclcpp_components::TestComponentBar";
auto result = composition_client->async_send_request(request);
auto ret = exec->spin_until_future_complete(result, 5s); // Wait for the result.
auto future = composition_client->async_send_request(request);
auto ret = exec->spin_until_future_complete(future, 5s); // Wait for the result.
auto result = future.get();
EXPECT_EQ(ret, rclcpp::FutureReturnCode::SUCCESS);
EXPECT_EQ(result.get()->success, true);
EXPECT_EQ(result.get()->error_message, "");
EXPECT_EQ(result.get()->full_node_name, "/test_component_bar");
EXPECT_EQ(result.get()->unique_id, 2u);
EXPECT_EQ(result->success, true);
EXPECT_EQ(result->error_message, "");
EXPECT_EQ(result->full_node_name, "/test_component_bar");
EXPECT_EQ(result->unique_id, 2u);
}
// Test remapping the node name
@@ -87,13 +89,14 @@ TEST_F(TestComponentManager, components_api)
request->plugin_name = "test_rclcpp_components::TestComponentFoo";
request->node_name = "test_component_baz";
auto result = composition_client->async_send_request(request);
auto ret = exec->spin_until_future_complete(result, 5s); // Wait for the result.
auto future = composition_client->async_send_request(request);
auto ret = exec->spin_until_future_complete(future, 5s); // Wait for the result.
auto result = future.get();
EXPECT_EQ(ret, rclcpp::FutureReturnCode::SUCCESS);
EXPECT_EQ(result.get()->success, true);
EXPECT_EQ(result.get()->error_message, "");
EXPECT_EQ(result.get()->full_node_name, "/test_component_baz");
EXPECT_EQ(result.get()->unique_id, 3u);
EXPECT_EQ(result->success, true);
EXPECT_EQ(result->error_message, "");
EXPECT_EQ(result->full_node_name, "/test_component_baz");
EXPECT_EQ(result->unique_id, 3u);
}
// Test remapping the node namespace
@@ -104,13 +107,14 @@ TEST_F(TestComponentManager, components_api)
request->node_namespace = "/ns";
request->node_name = "test_component_bing";
auto result = composition_client->async_send_request(request);
auto ret = exec->spin_until_future_complete(result, 5s); // Wait for the result.
auto future = composition_client->async_send_request(request);
auto ret = exec->spin_until_future_complete(future, 5s); // Wait for the result.
auto result = future.get();
EXPECT_EQ(ret, rclcpp::FutureReturnCode::SUCCESS);
EXPECT_EQ(result.get()->success, true);
EXPECT_EQ(result.get()->error_message, "");
EXPECT_EQ(result.get()->full_node_name, "/ns/test_component_bing");
EXPECT_EQ(result.get()->unique_id, 4u);
EXPECT_EQ(result->success, true);
EXPECT_EQ(result->error_message, "");
EXPECT_EQ(result->full_node_name, "/ns/test_component_bing");
EXPECT_EQ(result->unique_id, 4u);
}
{
@@ -119,13 +123,14 @@ TEST_F(TestComponentManager, components_api)
request->package_name = "rclcpp_components";
request->plugin_name = "test_rclcpp_components::TestComponent";
auto result = composition_client->async_send_request(request);
auto ret = exec->spin_until_future_complete(result, 5s); // Wait for the result.
auto future = composition_client->async_send_request(request);
auto ret = exec->spin_until_future_complete(future, 5s); // Wait for the result.
EXPECT_EQ(ret, rclcpp::FutureReturnCode::SUCCESS);
EXPECT_EQ(result.get()->success, false);
EXPECT_EQ(result.get()->error_message, "Failed to find class with the requested plugin name.");
EXPECT_EQ(result.get()->full_node_name, "");
EXPECT_EQ(result.get()->unique_id, 0u);
auto result = future.get();
EXPECT_EQ(result->success, false);
EXPECT_EQ(result->error_message, "Failed to find class with the requested plugin name.");
EXPECT_EQ(result->full_node_name, "");
EXPECT_EQ(result->unique_id, 0u);
}
{
@@ -134,13 +139,14 @@ TEST_F(TestComponentManager, components_api)
request->package_name = "rclcpp_components_foo";
request->plugin_name = "test_rclcpp_components::TestComponentFoo";
auto result = composition_client->async_send_request(request);
auto ret = exec->spin_until_future_complete(result, 5s); // Wait for the result.
auto future = composition_client->async_send_request(request);
auto ret = exec->spin_until_future_complete(future, 5s); // Wait for the result.
EXPECT_EQ(ret, rclcpp::FutureReturnCode::SUCCESS);
EXPECT_EQ(result.get()->success, false);
EXPECT_EQ(result.get()->error_message, "Could not find requested resource in ament index");
EXPECT_EQ(result.get()->full_node_name, "");
EXPECT_EQ(result.get()->unique_id, 0u);
auto result = future.get();
EXPECT_EQ(result->success, false);
EXPECT_EQ(result->error_message, "Could not find requested resource in ament index");
EXPECT_EQ(result->full_node_name, "");
EXPECT_EQ(result->unique_id, 0u);
}
{
@@ -151,13 +157,14 @@ TEST_F(TestComponentManager, components_api)
request->node_name = "test_component_remap";
request->remap_rules.push_back("alice:=bob");
auto result = composition_client->async_send_request(request);
auto ret = exec->spin_until_future_complete(result, 5s); // Wait for the result.
auto future = composition_client->async_send_request(request);
auto ret = exec->spin_until_future_complete(future, 5s); // Wait for the result.
auto result = future.get();
EXPECT_EQ(ret, rclcpp::FutureReturnCode::SUCCESS);
EXPECT_EQ(result.get()->success, true);
EXPECT_EQ(result.get()->error_message, "");
EXPECT_EQ(result.get()->full_node_name, "/test_component_remap");
EXPECT_EQ(result.get()->unique_id, 5u);
EXPECT_EQ(result->success, true);
EXPECT_EQ(result->error_message, "");
EXPECT_EQ(result->full_node_name, "/test_component_remap");
EXPECT_EQ(result->unique_id, 5u);
}
{
@@ -170,14 +177,15 @@ TEST_F(TestComponentManager, components_api)
rclcpp::ParameterValue(true));
request->extra_arguments.push_back(use_intraprocess_comms.to_parameter_msg());
auto result = composition_client->async_send_request(request);
auto ret = exec->spin_until_future_complete(result, 5s); // Wait for the result.
auto future = composition_client->async_send_request(request);
auto ret = exec->spin_until_future_complete(future, 5s); // Wait for the result.
auto result = future.get();
EXPECT_EQ(ret, rclcpp::FutureReturnCode::SUCCESS);
EXPECT_EQ(result.get()->success, true);
EXPECT_EQ(result.get()->error_message, "");
std::cout << result.get()->full_node_name << std::endl;
EXPECT_EQ(result.get()->full_node_name, "/test_component_intra_process");
EXPECT_EQ(result.get()->unique_id, 6u);
EXPECT_EQ(result->success, true);
EXPECT_EQ(result->error_message, "");
std::cout << result->full_node_name << std::endl;
EXPECT_EQ(result->full_node_name, "/test_component_intra_process");
EXPECT_EQ(result->unique_id, 6u);
}
{
@@ -191,15 +199,16 @@ TEST_F(TestComponentManager, components_api)
rclcpp::ParameterValue("hello"));
request->extra_arguments.push_back(use_intraprocess_comms.to_parameter_msg());
auto result = composition_client->async_send_request(request);
auto ret = exec->spin_until_future_complete(result, 5s); // Wait for the result.
auto future = composition_client->async_send_request(request);
auto ret = exec->spin_until_future_complete(future, 5s); // Wait for the result.
auto result = future.get();
EXPECT_EQ(ret, rclcpp::FutureReturnCode::SUCCESS);
EXPECT_EQ(result.get()->success, false);
EXPECT_EQ(result->success, false);
EXPECT_EQ(
result.get()->error_message,
result->error_message,
"Extra component argument 'use_intra_process_comms' must be a boolean");
EXPECT_EQ(result.get()->full_node_name, "");
EXPECT_EQ(result.get()->unique_id, 0u);
EXPECT_EQ(result->full_node_name, "");
EXPECT_EQ(result->unique_id, 0u);
}
auto node_names = node->get_node_names();
@@ -223,11 +232,12 @@ TEST_F(TestComponentManager, components_api)
{
auto request = std::make_shared<composition_interfaces::srv::ListNodes::Request>();
auto result = client->async_send_request(request);
auto ret = exec->spin_until_future_complete(result, 5s); // Wait for the result.
auto future = client->async_send_request(request);
auto ret = exec->spin_until_future_complete(future, 5s); // Wait for the result.
EXPECT_EQ(ret, rclcpp::FutureReturnCode::SUCCESS);
auto result_node_names = result.get()->full_node_names;
auto result_unique_ids = result.get()->unique_ids;
auto result = future.get();
auto result_node_names = result->full_node_names;
auto result_unique_ids = result->unique_ids;
EXPECT_EQ(result_node_names.size(), 6u);
EXPECT_EQ(result_node_names[0], "/test_component_foo");
@@ -258,22 +268,24 @@ TEST_F(TestComponentManager, components_api)
auto request = std::make_shared<composition_interfaces::srv::UnloadNode::Request>();
request->unique_id = 1;
auto result = client->async_send_request(request);
auto ret = exec->spin_until_future_complete(result, 5s); // Wait for the result.
auto future = client->async_send_request(request);
auto ret = exec->spin_until_future_complete(future, 5s); // Wait for the result.
auto result = future.get();
EXPECT_EQ(ret, rclcpp::FutureReturnCode::SUCCESS);
EXPECT_EQ(result.get()->success, true);
EXPECT_EQ(result.get()->error_message, "");
EXPECT_EQ(result->success, true);
EXPECT_EQ(result->error_message, "");
}
{
auto request = std::make_shared<composition_interfaces::srv::UnloadNode::Request>();
request->unique_id = 1;
auto result = client->async_send_request(request);
auto ret = exec->spin_until_future_complete(result, 5s); // Wait for the result.
auto future = client->async_send_request(request);
auto ret = exec->spin_until_future_complete(future, 5s); // Wait for the result.
auto result = future.get();
EXPECT_EQ(ret, rclcpp::FutureReturnCode::SUCCESS);
EXPECT_EQ(result.get()->success, false);
EXPECT_EQ(result.get()->error_message, "No node found with unique_id: 1");
EXPECT_EQ(result->success, false);
EXPECT_EQ(result->error_message, "No node found with unique_id: 1");
}
}
@@ -287,11 +299,12 @@ TEST_F(TestComponentManager, components_api)
{
auto request = std::make_shared<composition_interfaces::srv::ListNodes::Request>();
auto result = client->async_send_request(request);
auto ret = exec->spin_until_future_complete(result, 5s); // Wait for the result.
auto future = client->async_send_request(request);
auto ret = exec->spin_until_future_complete(future, 5s); // Wait for the result.
EXPECT_EQ(ret, rclcpp::FutureReturnCode::SUCCESS);
auto result_node_names = result.get()->full_node_names;
auto result_unique_ids = result.get()->unique_ids;
auto result = future.get();
auto result_node_names = result->full_node_names;
auto result_unique_ids = result->unique_ids;
EXPECT_EQ(result_node_names.size(), 5u);
EXPECT_EQ(result_node_names[0], "/test_component_bar");

View File

@@ -3,6 +3,46 @@ Changelog for package rclcpp_lifecycle
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
13.0.0 (2021-08-23)
-------------------
* Update client API to be able to remove pending requests. (`#1734 <https://github.com/ros2/rclcpp/issues/1734>`_)
* Change log level for lifecycle_publisher. (`#1715 <https://github.com/ros2/rclcpp/issues/1715>`_)
* Fix: RCLCPP_PUBLIC -> RCLCPP_LIFECYCLE_PUBLIC (`#1732 <https://github.com/ros2/rclcpp/issues/1732>`_)
* Use rcpputils/scope_exit.hpp and remove rclcpp/scope_exit.hpp (`#1727 <https://github.com/ros2/rclcpp/issues/1727>`_)
* Contributors: Alberto Soragna, Christophe Bedard, Ivan Santiago Paunovic, Shane Loretz
12.0.0 (2021-07-26)
-------------------
* Remove unsafe get_callback_groups API.
Callers should change to using for_each_callback_group(), or
store the callback groups they need internally.
* Add in callback_groups_for_each.
The main reason to add this method in is to make accesses to the
callback_groups\_ vector thread-safe. By having a
callback_groups_for_each that accepts a std::function, we can
just have the callers give us the callback they are interested
in, and we can take care of the locking.
The rest of this fairly large PR is cleaning up all of the places
that use get_callback_groups() to instead use
callback_groups_for_each().
* Contributors: Chris Lalancette
11.2.0 (2021-07-21)
-------------------
11.1.0 (2021-07-13)
-------------------
11.0.0 (2021-05-18)
-------------------
* Fix destruction order in lifecycle benchmark (`#1675 <https://github.com/ros2/rclcpp/issues/1675>`_)
* Contributors: Scott K Logan
10.0.0 (2021-05-11)
-------------------
* [rclcpp] Type Adaptation feature (`#1557 <https://github.com/ros2/rclcpp/issues/1557>`_)
* Contributors: Audrow Nash, William Woodall
9.0.2 (2021-04-14)
------------------

View File

@@ -103,6 +103,7 @@ if(BUILD_TESTING)
ament_target_dependencies(test_lifecycle_service_client
"rcl_lifecycle"
"rclcpp"
"rcpputils"
"rcutils"
)
target_link_libraries(test_lifecycle_service_client ${PROJECT_NAME} mimick)

View File

@@ -192,13 +192,11 @@ public:
rclcpp::CallbackGroupType group_type,
bool automatically_add_to_executor_with_node = true);
/// Return the list of callback groups in the node.
/**
* \return list of callback groups in the node.
*/
/// Iterate over the callback groups in the node, calling func on each valid one.
RCLCPP_LIFECYCLE_PUBLIC
const std::vector<rclcpp::CallbackGroup::WeakPtr> &
get_callback_groups() const;
void
for_each_callback_group(
const rclcpp::node_interfaces::NodeBaseInterface::CallbackGroupFunction & func);
/// Create and return a Publisher.
/**
@@ -230,13 +228,8 @@ public:
typename MessageT,
typename CallbackT,
typename AllocatorT = std::allocator<void>,
typename CallbackMessageT =
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type,
typename SubscriptionT = rclcpp::Subscription<MessageT, AllocatorT>,
typename MessageMemoryStrategyT = rclcpp::message_memory_strategy::MessageMemoryStrategy<
CallbackMessageT,
AllocatorT
>>
typename MessageMemoryStrategyT = typename SubscriptionT::MessageMemoryStrategyType>
std::shared_ptr<SubscriptionT>
create_subscription(
const std::string & topic_name,

View File

@@ -61,7 +61,6 @@ template<
typename MessageT,
typename CallbackT,
typename AllocatorT,
typename CallbackMessageT,
typename SubscriptionT,
typename MessageMemoryStrategyT>
std::shared_ptr<SubscriptionT>

View File

@@ -65,6 +65,7 @@ public:
const rclcpp::PublisherOptionsWithAllocator<Alloc> & options)
: rclcpp::Publisher<MessageT, Alloc>(node_base, topic, qos, options),
enabled_(false),
should_log_(true),
logger_(rclcpp::get_logger("LifecyclePublisher"))
{
}
@@ -81,11 +82,7 @@ public:
publish(std::unique_ptr<MessageT, MessageDeleter> msg)
{
if (!enabled_) {
RCLCPP_WARN(
logger_,
"Trying to publish message on the topic '%s', but the publisher is not activated",
this->get_topic_name());
log_publisher_not_enabled();
return;
}
rclcpp::Publisher<MessageT, Alloc>::publish(std::move(msg));
@@ -101,11 +98,7 @@ public:
publish(const MessageT & msg)
{
if (!enabled_) {
RCLCPP_WARN(
logger_,
"Trying to publish message on the topic '%s', but the publisher is not activated",
this->get_topic_name());
log_publisher_not_enabled();
return;
}
rclcpp::Publisher<MessageT, Alloc>::publish(msg);
@@ -121,6 +114,7 @@ public:
on_deactivate()
{
enabled_ = false;
should_log_ = true;
}
virtual bool
@@ -130,7 +124,30 @@ public:
}
private:
/// LifecyclePublisher log helper function
/**
* Helper function that logs a message saying that publisher can't publish
* because it's not enabled.
*/
void log_publisher_not_enabled()
{
// Nothing to do if we are not meant to log
if (!should_log_) {
return;
}
// Log the message
RCLCPP_WARN(
logger_,
"Trying to publish message on the topic '%s', but the publisher is not activated",
this->get_topic_name());
// We stop logging until the flag gets enabled again
should_log_ = false;
}
bool enabled_ = false;
bool should_log_ = true;
rclcpp::Logger logger_;
};

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>9.0.2</version>
<version>13.0.0</version>
<description>Package containing a prototype for lifecycle implementation</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
<maintainer email="mabel@openrobotics.org">Mabel Zhang</maintainer>
@@ -29,6 +29,7 @@
<test_depend>ament_lint_common</test_depend>
<test_depend>mimick_vendor</test_depend>
<test_depend>performance_test_fixture</test_depend>
<test_depend>rcpputils</test_depend>
<test_depend>rcutils</test_depend>
<test_depend>test_msgs</test_depend>

View File

@@ -365,10 +365,11 @@ LifecycleNode::get_subscriptions_info_by_topic(const std::string & topic_name, b
return node_graph_->get_subscriptions_info_by_topic(topic_name, no_mangle);
}
const std::vector<rclcpp::CallbackGroup::WeakPtr> &
LifecycleNode::get_callback_groups() const
void
LifecycleNode::for_each_callback_group(
const rclcpp::node_interfaces::NodeBaseInterface::CallbackGroupFunction & func)
{
return node_base_->get_callback_groups();
node_base_->for_each_callback_group(func);
}
rclcpp::Event::SharedPtr

Some files were not shown because too many files have changed in this diff Show More