Compare commits
98 Commits
rcl_alloca
...
14.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2d6e6364cd | ||
|
|
5613d613cf | ||
|
|
7a2ee23281 | ||
|
|
802bfc2c74 | ||
|
|
152dbc6e38 | ||
|
|
9342c257b8 | ||
|
|
d2e563afd5 | ||
|
|
8ac848bbc2 | ||
|
|
b2b676d317 | ||
|
|
ee20dd31e6 | ||
|
|
0775e2f6e7 | ||
|
|
321c74c2b3 | ||
|
|
536df11ee0 | ||
|
|
6f6e9e8ce9 | ||
|
|
39dad4d9a7 | ||
|
|
87d754b219 | ||
|
|
e03e98220d | ||
|
|
f7bb88fc8f | ||
|
|
0781ea543c | ||
|
|
e0e96681d9 | ||
|
|
b1f31e0eaa | ||
|
|
e2aeb1028b | ||
|
|
94264320b4 | ||
|
|
b135e89c1e | ||
|
|
c59793618a | ||
|
|
5ecc5b6c19 | ||
|
|
2d7bd9f4cb | ||
|
|
0fd866d201 | ||
|
|
82950f1141 | ||
|
|
4a343a1f23 | ||
|
|
a569214273 | ||
|
|
942b74c8bd | ||
|
|
d107a844ea | ||
|
|
301957515a | ||
|
|
d04319a438 | ||
|
|
d5ec258080 | ||
|
|
9e445bdb91 | ||
|
|
fa3a6fa597 | ||
|
|
81df5843f3 | ||
|
|
f3a5187775 | ||
|
|
2801553d61 | ||
|
|
001f0fb620 | ||
|
|
665e37784a | ||
|
|
ecb81ef2c3 | ||
|
|
d0cd6bb0a4 | ||
|
|
fd08f0dbe7 | ||
|
|
2dd09ae274 | ||
|
|
679fb2ba33 | ||
|
|
4fcd05db72 | ||
|
|
d7764b4322 | ||
|
|
893b9b4f82 | ||
|
|
b918bd4c25 | ||
|
|
3b1144f1e0 | ||
|
|
6c0a46bcc8 | ||
|
|
3cddb4edab | ||
|
|
d5f3d35fbe | ||
|
|
bf752c75f5 | ||
|
|
01f6ebdd3d | ||
|
|
7bf52dd8a6 | ||
|
|
1b28f389c2 | ||
|
|
e8cbfe6a1b | ||
|
|
0c01a43a4f | ||
|
|
133088e9a3 | ||
|
|
3d42c9a5df | ||
|
|
5c4f809f2a | ||
|
|
f7a301441a | ||
|
|
00f2d563be | ||
|
|
64ee7d6822 | ||
|
|
0750dc418a | ||
|
|
0d6d9e6778 | ||
|
|
86c079de31 | ||
|
|
fb8519070c | ||
|
|
e1095adeee | ||
|
|
4ecb3dd090 | ||
|
|
0034929eef | ||
|
|
dbb717cd6e | ||
|
|
e9e398d2d4 | ||
|
|
7d8b2690ff | ||
|
|
55d2f67b90 | ||
|
|
39bfb30eb0 | ||
|
|
1c61751540 | ||
|
|
0251f70fe8 | ||
|
|
07b6ea0ff4 | ||
|
|
56f68f9c44 | ||
|
|
18fcd9138b | ||
|
|
f245b4cc81 | ||
|
|
d488535f36 | ||
|
|
72443ff295 | ||
|
|
a3383c309a | ||
|
|
5dad229662 | ||
|
|
2e4c97ac56 | ||
|
|
0659d829ce | ||
|
|
1fc2d58799 | ||
|
|
c15eb1eaac | ||
|
|
d051b8aa20 | ||
|
|
6806cdf825 | ||
|
|
79c2dd8e8b | ||
|
|
fba080cf34 |
@@ -2,6 +2,120 @@
|
||||
Changelog for package rclcpp
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
14.1.0 (2022-01-05)
|
||||
-------------------
|
||||
* Use UninitializedStaticallyTypedParameterException (`#1689 <https://github.com/ros2/rclcpp/issues/1689>`_)
|
||||
* Add wait_for_all_acked support (`#1662 <https://github.com/ros2/rclcpp/issues/1662>`_)
|
||||
* Add tests for function templates of declare_parameter (`#1747 <https://github.com/ros2/rclcpp/issues/1747>`_)
|
||||
* Contributors: Barry Xu, Bi0T1N, M. Mostafa Farzan
|
||||
|
||||
14.0.0 (2021-12-17)
|
||||
-------------------
|
||||
* Fixes for uncrustify 0.72 (`#1844 <https://github.com/ros2/rclcpp/issues/1844>`_)
|
||||
* use private member to keep the all reference underneath. (`#1845 <https://github.com/ros2/rclcpp/issues/1845>`_)
|
||||
* Make node base sharable (`#1832 <https://github.com/ros2/rclcpp/issues/1832>`_)
|
||||
* Add Clock::sleep_for() (`#1828 <https://github.com/ros2/rclcpp/issues/1828>`_)
|
||||
* Synchronize rcl and std::chrono steady clocks in Clock::sleep_until (`#1830 <https://github.com/ros2/rclcpp/issues/1830>`_)
|
||||
* Use rclcpp::guard_condition (`#1612 <https://github.com/ros2/rclcpp/issues/1612>`_)
|
||||
* Call CMake function to generate version header (`#1805 <https://github.com/ros2/rclcpp/issues/1805>`_)
|
||||
* Use parantheses around logging macro parameter (`#1820 <https://github.com/ros2/rclcpp/issues/1820>`_)
|
||||
* Remove author by request (`#1818 <https://github.com/ros2/rclcpp/issues/1818>`_)
|
||||
* Update maintainers (`#1817 <https://github.com/ros2/rclcpp/issues/1817>`_)
|
||||
* min_forward & min_backward thresholds must not be disabled (`#1815 <https://github.com/ros2/rclcpp/issues/1815>`_)
|
||||
* Re-add Clock::sleep_until (`#1814 <https://github.com/ros2/rclcpp/issues/1814>`_)
|
||||
* Fix lifetime of context so it remains alive while its dependent node handles are still in use (`#1754 <https://github.com/ros2/rclcpp/issues/1754>`_)
|
||||
* Add the interface for pre-shutdown callback (`#1714 <https://github.com/ros2/rclcpp/issues/1714>`_)
|
||||
* Take message ownership from moved LoanedMessage (`#1808 <https://github.com/ros2/rclcpp/issues/1808>`_)
|
||||
* Suppress clang dead-store warnings in the benchmarks. (`#1802 <https://github.com/ros2/rclcpp/issues/1802>`_)
|
||||
* Wait for publisher and subscription to match (`#1777 <https://github.com/ros2/rclcpp/issues/1777>`_)
|
||||
* Fix unused QoS profile for clock subscription and make ClockQoS the default (`#1801 <https://github.com/ros2/rclcpp/issues/1801>`_)
|
||||
* Contributors: Abrar Rahman Protyasha, Barry Xu, Chen Lihui, Chris Lalancette, Grey, Jacob Perron, Nikolai Morin, Shane Loretz, Tomoya Fujita, mauropasse
|
||||
|
||||
13.1.0 (2021-10-18)
|
||||
-------------------
|
||||
* Fix dangerous std::bind capture in TimeSource implementation. (`#1768 <https://github.com/ros2/rclcpp/issues/1768>`_)
|
||||
* Fix dangerous std::bind capture in ParameterEventHandler implementation. (`#1770 <https://github.com/ros2/rclcpp/issues/1770>`_)
|
||||
* Handle sigterm, in the same way sigint is being handled. (`#1771 <https://github.com/ros2/rclcpp/issues/1771>`_)
|
||||
* rclcpp::Node copy constructor: make copy of node_waitables\_ member. (`#1799 <https://github.com/ros2/rclcpp/issues/1799>`_)
|
||||
* Extend NodeGraph to match what rcl provides. (`#1484 <https://github.com/ros2/rclcpp/issues/1484>`_)
|
||||
* Context::sleep_for(): replace recursion with do-while to avoid potential stack-overflow. (`#1765 <https://github.com/ros2/rclcpp/issues/1765>`_)
|
||||
* extend_sub_namespace(): Verify string::empty() before calling string::front(). (`#1764 <https://github.com/ros2/rclcpp/issues/1764>`_)
|
||||
* Deprecate the `void shared_ptr<MessageT>` subscription callback signatures. (`#1713 <https://github.com/ros2/rclcpp/issues/1713>`_)
|
||||
* Contributors: Abrar Rahman Protyasha, Chris Lalancette, Emerson Knapp, Geoffrey Biggs, Ivan Santiago Paunovic, Jorge Perez, Tomoya Fujita, William Woodall, Yong-Hao Zou, livanov93
|
||||
|
||||
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>`_)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
cmake_minimum_required(VERSION 3.12)
|
||||
|
||||
project(rclcpp)
|
||||
|
||||
@@ -41,7 +41,7 @@ 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/add_guard_condition_to_rcl_wait_set.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 +110,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 +125,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 +149,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 +169,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
|
||||
@@ -252,3 +254,5 @@ if(TEST cppcheck)
|
||||
# must set the property after ament_package()
|
||||
set_tests_properties(cppcheck PROPERTIES TIMEOUT 500)
|
||||
endif()
|
||||
|
||||
ament_cmake_gen_version_h()
|
||||
|
||||
@@ -29,23 +29,71 @@ namespace allocator
|
||||
template<typename T, typename Alloc>
|
||||
using AllocRebind = typename std::allocator_traits<Alloc>::template rebind_traits<T>;
|
||||
|
||||
/// Return the equivalent rcl_allocator_t for the C++ standard allocator.
|
||||
/**
|
||||
* This assumes that the user intent behind both allocators is the
|
||||
* same: Using system malloc for allocation.
|
||||
*
|
||||
* If you're using a custom allocator in ROS, you'll need to provide
|
||||
* your own overload for this function.
|
||||
*/
|
||||
template<typename T>
|
||||
rcl_allocator_t get_rcl_allocator(std::allocator<T> allocator)
|
||||
template<typename Alloc>
|
||||
void * retyped_allocate(size_t size, void * untyped_allocator)
|
||||
{
|
||||
auto typed_allocator = static_cast<Alloc *>(untyped_allocator);
|
||||
if (!typed_allocator) {
|
||||
throw std::runtime_error("Received incorrect allocator type");
|
||||
}
|
||||
return std::allocator_traits<Alloc>::allocate(*typed_allocator, size);
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
void retyped_deallocate(void * untyped_pointer, void * untyped_allocator)
|
||||
{
|
||||
auto typed_allocator = static_cast<Alloc *>(untyped_allocator);
|
||||
if (!typed_allocator) {
|
||||
throw std::runtime_error("Received incorrect allocator type");
|
||||
}
|
||||
auto typed_ptr = static_cast<T *>(untyped_pointer);
|
||||
std::allocator_traits<Alloc>::deallocate(*typed_allocator, typed_ptr, 1);
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
void * retyped_reallocate(void * untyped_pointer, size_t size, void * untyped_allocator)
|
||||
{
|
||||
auto typed_allocator = static_cast<Alloc *>(untyped_allocator);
|
||||
if (!typed_allocator) {
|
||||
throw std::runtime_error("Received incorrect allocator type");
|
||||
}
|
||||
auto typed_ptr = static_cast<T *>(untyped_pointer);
|
||||
std::allocator_traits<Alloc>::deallocate(*typed_allocator, typed_ptr, 1);
|
||||
return std::allocator_traits<Alloc>::allocate(*typed_allocator, size);
|
||||
}
|
||||
|
||||
|
||||
// Convert a std::allocator_traits-formatted Allocator into an rcl allocator
|
||||
template<
|
||||
typename T,
|
||||
typename Alloc,
|
||||
typename std::enable_if<!std::is_same<Alloc, std::allocator<void>>::value>::type * = nullptr>
|
||||
rcl_allocator_t get_rcl_allocator(Alloc & allocator)
|
||||
{
|
||||
rcl_allocator_t rcl_allocator = rcl_get_default_allocator();
|
||||
#ifndef _WIN32
|
||||
rcl_allocator.allocate = &retyped_allocate<Alloc>;
|
||||
rcl_allocator.deallocate = &retyped_deallocate<T, Alloc>;
|
||||
rcl_allocator.reallocate = &retyped_reallocate<T, Alloc>;
|
||||
rcl_allocator.state = &allocator;
|
||||
#else
|
||||
(void)allocator; // Remove warning
|
||||
#endif
|
||||
return rcl_allocator;
|
||||
}
|
||||
|
||||
// TODO(jacquelinekay) Workaround for an incomplete implementation of std::allocator<void>
|
||||
template<
|
||||
typename T,
|
||||
typename Alloc,
|
||||
typename std::enable_if<std::is_same<Alloc, std::allocator<void>>::value>::type * = nullptr>
|
||||
rcl_allocator_t get_rcl_allocator(Alloc & allocator)
|
||||
{
|
||||
(void)allocator;
|
||||
return rcl_get_default_allocator();
|
||||
}
|
||||
|
||||
} // namespace allocator
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__ALLOCATOR__ALLOCATOR_COMMON_HPP_
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,81 @@ public:
|
||||
}
|
||||
|
||||
/// Function for shared_ptr to non-const MessageT, which is deprecated.
|
||||
// TODO(wjwwood): enable this deprecation after Galactic
|
||||
// [[deprecated(
|
||||
// "use 'void (std::shared_ptr<const MessageT>)' instead"
|
||||
// )]]
|
||||
template<typename SetT>
|
||||
#if !defined(RCLCPP_AVOID_DEPRECATIONS_FOR_UNIT_TESTS)
|
||||
// suppress deprecation warnings in `test_any_subscription_callback.cpp`
|
||||
[[deprecated("use 'void(std::shared_ptr<const MessageT>)' instead")]]
|
||||
#endif
|
||||
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.
|
||||
// TODO(wjwwood): enable this deprecation after Galactic
|
||||
// [[deprecated(
|
||||
// "use 'void (std::shared_ptr<const MessageT>, const rclcpp::MessageInfo &)' instead"
|
||||
// )]]
|
||||
template<typename SetT>
|
||||
#if !defined(RCLCPP_AVOID_DEPRECATIONS_FOR_UNIT_TESTS)
|
||||
// suppress deprecation warnings in `test_any_subscription_callback.cpp`
|
||||
[[deprecated(
|
||||
"use 'void(std::shared_ptr<const MessageT>, const rclcpp::MessageInfo &)' instead"
|
||||
)]]
|
||||
#endif
|
||||
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 +494,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 +658,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 +673,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 +764,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 +777,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 +879,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 +924,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
|
||||
|
||||
@@ -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_;
|
||||
};
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#include "rclcpp/contexts/default_context.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/time.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
@@ -78,6 +79,64 @@ public:
|
||||
Time
|
||||
now();
|
||||
|
||||
/**
|
||||
* Sleep until a specified Time, according to clock type.
|
||||
*
|
||||
* Notes for RCL_ROS_TIME clock type:
|
||||
* - Can sleep forever if ros time is active and received clock never reaches `until`
|
||||
* - If ROS time enabled state changes during the sleep, this method will immediately return
|
||||
* false. There is not a consistent choice of sleeping time when the time source changes,
|
||||
* so this is up to the caller to call again if needed.
|
||||
*
|
||||
* \warning When using gcc < 10 or when using gcc >= 10 and pthreads lacks the function
|
||||
* `pthread_cond_clockwait`, steady clocks may sleep using the system clock.
|
||||
* If so, steady clock sleep times can be affected by system clock time jumps.
|
||||
* Depending on the steady clock's epoch and resolution in comparison to the system clock's,
|
||||
* an overflow when converting steady clock durations to system clock times may cause
|
||||
* undefined behavior.
|
||||
* For more info see these issues:
|
||||
* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=41861
|
||||
* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58931
|
||||
*
|
||||
* \param until absolute time according to current clock type to sleep until.
|
||||
* \param context the rclcpp context the clock should use to check that ROS is still initialized.
|
||||
* \return true immediately if `until` is in the past
|
||||
* \return true when the time `until` is reached
|
||||
* \return false if time cannot be reached reliably, for example from shutdown or a change
|
||||
* of time source.
|
||||
* \throws std::runtime_error if the context is invalid
|
||||
* \throws std::runtime_error if `until` has a different clock type from this clock
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
sleep_until(
|
||||
Time until,
|
||||
Context::SharedPtr context = contexts::get_global_default_context());
|
||||
|
||||
/**
|
||||
* Sleep for a specified Duration.
|
||||
*
|
||||
* Equivalent to
|
||||
*
|
||||
* ```cpp
|
||||
* clock->sleep_until(clock->now() + rel_time, context)
|
||||
* ```
|
||||
*
|
||||
* The function will return immediately if `rel_time` is zero or negative.
|
||||
*
|
||||
* \param rel_time the length of time to sleep for.
|
||||
* \param context the rclcpp context the clock should use to check that ROS is still initialized.
|
||||
* \return true when the end time is reached
|
||||
* \return false if time cannot be reached reliably, for example from shutdown or a change
|
||||
* of time source.
|
||||
* \throws std::runtime_error if the context is invalid
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
sleep_for(
|
||||
Duration rel_time,
|
||||
Context::SharedPtr context = contexts::get_global_default_context());
|
||||
|
||||
/**
|
||||
* Returns the clock of the type `RCL_ROS_TIME` is active.
|
||||
*
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <typeindex>
|
||||
#include <typeinfo>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -47,6 +48,20 @@ public:
|
||||
/// Forward declare WeakContextsWrapper
|
||||
class WeakContextsWrapper;
|
||||
|
||||
class ShutdownCallbackHandle
|
||||
{
|
||||
friend class Context;
|
||||
|
||||
public:
|
||||
using ShutdownCallbackType = std::function<void ()>;
|
||||
|
||||
private:
|
||||
std::weak_ptr<ShutdownCallbackType> callback;
|
||||
};
|
||||
|
||||
using OnShutdownCallbackHandle = ShutdownCallbackHandle;
|
||||
using PreShutdownCallbackHandle = ShutdownCallbackHandle;
|
||||
|
||||
/// Context which encapsulates shared state between nodes and other similar entities.
|
||||
/**
|
||||
* A context also represents the lifecycle between init and shutdown of rclcpp.
|
||||
@@ -63,7 +78,7 @@ public:
|
||||
* Every context which is constructed is added to a global vector of contexts,
|
||||
* which is used by the signal handler to conditionally shutdown each context
|
||||
* on SIGINT.
|
||||
* See the shutdown_on_sigint option in the InitOptions class.
|
||||
* See the shutdown_on_signal option in the InitOptions class.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
Context();
|
||||
@@ -80,8 +95,8 @@ 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().
|
||||
* In addition to installing the signal handlers, the shutdown_on_sigint
|
||||
* them manually with rclcpp::install_signal_handlers() or use rclcpp::init().
|
||||
* In addition to installing the signal handlers, the shutdown_on_signal
|
||||
* 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 +192,7 @@ public:
|
||||
bool
|
||||
shutdown(const std::string & reason);
|
||||
|
||||
using OnShutdownCallback = std::function<void ()>;
|
||||
using OnShutdownCallback = OnShutdownCallbackHandle::ShutdownCallbackType;
|
||||
|
||||
/// Add a on_shutdown callback to be called when shutdown is called for this context.
|
||||
/**
|
||||
@@ -185,7 +200,7 @@ public:
|
||||
* to last step in shutdown().
|
||||
*
|
||||
* When shutdown occurs due to the signal handler, these callbacks are run
|
||||
* asynchronoulsy in the dedicated singal handling thread.
|
||||
* 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.
|
||||
@@ -203,23 +218,82 @@ 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 signal 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);
|
||||
|
||||
using PreShutdownCallback = PreShutdownCallbackHandle::ShutdownCallbackType;
|
||||
|
||||
/// Add a pre_shutdown callback to be called before shutdown is called for this context.
|
||||
/**
|
||||
* These callbacks will be called in the order they are added.
|
||||
*
|
||||
* When shutdown occurs due to the signal handler, these callbacks are run
|
||||
* asynchronously in the dedicated signal handling thread.
|
||||
*
|
||||
* \param[in] callback the pre_shutdown callback to be registered
|
||||
* \return the created callback handle
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
PreShutdownCallbackHandle
|
||||
add_pre_shutdown_callback(PreShutdownCallback callback);
|
||||
|
||||
/// Remove an registered pre_shutdown callback.
|
||||
/**
|
||||
* \param[in] callback_handle the pre_shutdown callback handle to be removed.
|
||||
* \return true if the callback is found and removed, otherwise false.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
bool
|
||||
remove_pre_shutdown_callback(const PreShutdownCallbackHandle & 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 pre-shutdown callbacks.
|
||||
/**
|
||||
* Returned callbacks are a copy of the registered callbacks.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<PreShutdownCallback>
|
||||
get_pre_shutdown_callbacks() const;
|
||||
|
||||
/// Return the internal rcl context.
|
||||
RCLCPP_PUBLIC
|
||||
@@ -232,7 +306,7 @@ public:
|
||||
*
|
||||
* - this context is shutdown()
|
||||
* - this context is destructed (resulting in shutdown)
|
||||
* - this context has shutdown_on_sigint=true and SIGINT occurs (resulting in shutdown)
|
||||
* - this context has shutdown_on_signal=true and SIGINT/SIGTERM occurs (resulting in shutdown)
|
||||
* - interrupt_all_sleep_for() is called
|
||||
*
|
||||
* \param[in] nanoseconds A std::chrono::duration representing how long to sleep for.
|
||||
@@ -299,8 +373,11 @@ 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_;
|
||||
|
||||
std::unordered_set<std::shared_ptr<PreShutdownCallback>> pre_shutdown_callbacks_;
|
||||
mutable std::mutex pre_shutdown_callbacks_mutex_;
|
||||
|
||||
/// Condition variable for timed sleep (see sleep_for).
|
||||
std::condition_variable interrupt_condition_variable_;
|
||||
@@ -309,6 +386,29 @@ private:
|
||||
|
||||
/// Keep shared ownership of global vector of weak contexts
|
||||
std::shared_ptr<WeakContextsWrapper> weak_contexts_;
|
||||
|
||||
enum class ShutdownType
|
||||
{
|
||||
pre_shutdown,
|
||||
on_shutdown
|
||||
};
|
||||
|
||||
using ShutdownCallback = ShutdownCallbackHandle::ShutdownCallbackType;
|
||||
|
||||
RCLCPP_LOCAL
|
||||
ShutdownCallbackHandle
|
||||
add_shutdown_callback(
|
||||
ShutdownType shutdown_type,
|
||||
ShutdownCallback callback);
|
||||
|
||||
RCLCPP_LOCAL
|
||||
bool
|
||||
remove_shutdown_callback(
|
||||
ShutdownType shutdown_type,
|
||||
const ShutdownCallbackHandle & callback_handle);
|
||||
|
||||
std::vector<rclcpp::Context::ShutdownCallback>
|
||||
get_shutdown_callback(ShutdownType shutdown_type) const;
|
||||
};
|
||||
|
||||
/// Return a copy of the list of context shared pointers.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
// 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__ADD_GUARD_CONDITION_TO_RCL_WAIT_SET_HPP_
|
||||
#define RCLCPP__DETAIL__ADD_GUARD_CONDITION_TO_RCL_WAIT_SET_HPP_
|
||||
|
||||
#include "rclcpp/guard_condition.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
|
||||
/// Adds the guard condition to a waitset
|
||||
/**
|
||||
* \param[in] wait_set reference to a wait set where to add the guard condition
|
||||
* \param[in] guard_condition reference to the guard_condition to be added
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_guard_condition_to_rcl_wait_set(
|
||||
rcl_wait_set_t & wait_set,
|
||||
const rclcpp::GuardCondition & guard_condition);
|
||||
|
||||
} // namespace detail
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__DETAIL__ADD_GUARD_CONDITION_TO_RCL_WAIT_SET_HPP_
|
||||
@@ -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_
|
||||
@@ -254,6 +254,23 @@ public:
|
||||
{}
|
||||
};
|
||||
|
||||
/// Thrown if user attempts to create an uninitialized statically typed parameter
|
||||
/**
|
||||
* (see https://github.com/ros2/rclcpp/issues/1691)
|
||||
*/
|
||||
class UninitializedStaticallyTypedParameterException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
/// Construct an instance.
|
||||
/**
|
||||
* \param[in] name the name of the parameter.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
explicit UninitializedStaticallyTypedParameterException(const std::string & name)
|
||||
: std::runtime_error("Statically typed parameter '" + name + "' must be initialized.")
|
||||
{}
|
||||
};
|
||||
|
||||
/// Thrown if parameter is already declared.
|
||||
class ParameterAlreadyDeclaredException : public std::runtime_error
|
||||
{
|
||||
@@ -282,8 +299,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 +308,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")
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
@@ -524,7 +525,7 @@ protected:
|
||||
std::atomic_bool spinning;
|
||||
|
||||
/// Guard condition for signaling the rmw layer to wake up for special events.
|
||||
rcl_guard_condition_t interrupt_guard_condition_ = rcl_get_zero_initialized_guard_condition();
|
||||
rclcpp::GuardCondition interrupt_guard_condition_;
|
||||
|
||||
std::shared_ptr<rclcpp::GuardCondition> shutdown_guard_condition_;
|
||||
|
||||
@@ -548,7 +549,7 @@ protected:
|
||||
spin_once_impl(std::chrono::nanoseconds timeout);
|
||||
|
||||
typedef std::map<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr,
|
||||
const rcl_guard_condition_t *,
|
||||
const rclcpp::GuardCondition *,
|
||||
std::owner_less<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>>
|
||||
WeakNodesToGuardConditionsMap;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -104,7 +102,7 @@ public:
|
||||
* \throws std::runtime_error if it couldn't add guard condition to wait set
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
void
|
||||
add_to_wait_set(rcl_wait_set_t * wait_set) override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
@@ -330,7 +328,7 @@ private:
|
||||
WeakCallbackGroupsToNodesMap weak_groups_to_nodes_associated_with_executor_;
|
||||
|
||||
typedef std::map<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr,
|
||||
const rcl_guard_condition_t *,
|
||||
const rclcpp::GuardCondition *,
|
||||
std::owner_less<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>>
|
||||
WeakNodesToGuardConditionsMap;
|
||||
WeakNodesToGuardConditionsMap weak_nodes_to_guard_conditions_;
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
|
||||
#include "rcl/error_handling.h"
|
||||
|
||||
#include "rclcpp/guard_condition.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rclcpp/type_support_decl.hpp"
|
||||
#include "rclcpp/waitable.hpp"
|
||||
|
||||
@@ -39,8 +41,11 @@ public:
|
||||
RCLCPP_SMART_PTR_ALIASES_ONLY(SubscriptionIntraProcessBase)
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
SubscriptionIntraProcessBase(const std::string & topic_name, rmw_qos_profile_t qos_profile)
|
||||
: topic_name_(topic_name), qos_profile_(qos_profile)
|
||||
SubscriptionIntraProcessBase(
|
||||
rclcpp::Context::SharedPtr context,
|
||||
const std::string & topic_name,
|
||||
const rclcpp::QoS & qos_profile)
|
||||
: gc_(context), topic_name_(topic_name), qos_profile_(qos_profile)
|
||||
{}
|
||||
|
||||
virtual ~SubscriptionIntraProcessBase() = default;
|
||||
@@ -50,7 +55,7 @@ public:
|
||||
get_number_of_ready_guard_conditions() {return 1;}
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
void
|
||||
add_to_wait_set(rcl_wait_set_t * wait_set);
|
||||
|
||||
virtual bool
|
||||
@@ -71,19 +76,19 @@ public:
|
||||
get_topic_name() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
rmw_qos_profile_t
|
||||
QoS
|
||||
get_actual_qos() const;
|
||||
|
||||
protected:
|
||||
std::recursive_mutex reentrant_mutex_;
|
||||
rcl_guard_condition_t gc_;
|
||||
rclcpp::GuardCondition gc_;
|
||||
|
||||
private:
|
||||
virtual void
|
||||
trigger_guard_condition() = 0;
|
||||
|
||||
std::string topic_name_;
|
||||
rmw_qos_profile_t qos_profile_;
|
||||
QoS qos_profile_;
|
||||
};
|
||||
|
||||
} // namespace experimental
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
// 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(context, topic_name, qos_profile)
|
||||
{
|
||||
// Create the intra-process buffer.
|
||||
buffer_ = rclcpp::experimental::create_intra_process_buffer<MessageT, Alloc, Deleter>(
|
||||
buffer_type,
|
||||
qos_profile,
|
||||
allocator);
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
gc_.trigger();
|
||||
}
|
||||
|
||||
BufferUniquePtr buffer_;
|
||||
};
|
||||
|
||||
} // namespace experimental
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_BUFFER_HPP_
|
||||
@@ -83,7 +83,7 @@ template<typename ClassT, typename ReturnTypeT, typename ... Args, typename ...
|
||||
#if defined _LIBCPP_VERSION // libc++ (Clang)
|
||||
struct function_traits<std::__bind<ReturnTypeT (ClassT::*)(Args ...), FArgs ...>>
|
||||
#elif defined _GLIBCXX_RELEASE // glibc++ (GNU C++ >= 7.1)
|
||||
struct function_traits<std::_Bind<ReturnTypeT(ClassT::*(FArgs ...))(Args ...)>>
|
||||
struct function_traits<std::_Bind<ReturnTypeT(ClassT::* (FArgs ...))(Args ...)>>
|
||||
#elif defined __GLIBCXX__ // glibc++ (GNU C++)
|
||||
struct function_traits<std::_Bind<std::_Mem_fn<ReturnTypeT (ClassT::*)(Args ...)>(FArgs ...)>>
|
||||
#elif defined _MSC_VER // MS Visual Studio
|
||||
@@ -100,7 +100,7 @@ template<typename ClassT, typename ReturnTypeT, typename ... Args, typename ...
|
||||
#if defined _LIBCPP_VERSION // libc++ (Clang)
|
||||
struct function_traits<std::__bind<ReturnTypeT (ClassT::*)(Args ...) const, FArgs ...>>
|
||||
#elif defined _GLIBCXX_RELEASE // glibc++ (GNU C++ >= 7.1)
|
||||
struct function_traits<std::_Bind<ReturnTypeT(ClassT::*(FArgs ...))(Args ...) const>>
|
||||
struct function_traits<std::_Bind<ReturnTypeT(ClassT::* (FArgs ...))(Args ...) const>>
|
||||
#elif defined __GLIBCXX__ // glibc++ (GNU C++)
|
||||
struct function_traits<std::_Bind<std::_Mem_fn<ReturnTypeT (ClassT::*)(Args ...) const>(FArgs ...)>>
|
||||
#elif defined _MSC_VER // MS Visual Studio
|
||||
|
||||
@@ -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(
|
||||
|
||||
90
rclcpp/include/rclcpp/get_message_type_support_handle.hpp
Normal file
90
rclcpp/include/rclcpp/get_message_type_support_handle.hpp
Normal 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_
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "rcl/guard_condition.h"
|
||||
#include "rcl/wait.h"
|
||||
#include "rclcpp/context.hpp"
|
||||
#include "rclcpp/guard_condition.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node_interfaces/node_graph_interface.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
@@ -187,7 +188,7 @@ private:
|
||||
mutable std::mutex node_graph_interfaces_mutex_;
|
||||
std::vector<rclcpp::node_interfaces::NodeGraphInterface *> node_graph_interfaces_;
|
||||
|
||||
rcl_guard_condition_t interrupt_guard_condition_ = rcl_get_zero_initialized_guard_condition();
|
||||
rclcpp::GuardCondition interrupt_guard_condition_;
|
||||
rcl_wait_set_t wait_set_ = rcl_get_zero_initialized_wait_set();
|
||||
};
|
||||
|
||||
|
||||
@@ -47,7 +47,9 @@ public:
|
||||
RCLCPP_PUBLIC
|
||||
explicit GuardCondition(
|
||||
rclcpp::Context::SharedPtr context =
|
||||
rclcpp::contexts::get_global_default_context());
|
||||
rclcpp::contexts::get_global_default_context(),
|
||||
rcl_guard_condition_options_t guard_condition_options =
|
||||
rcl_guard_condition_get_default_options());
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
@@ -58,6 +60,11 @@ public:
|
||||
rclcpp::Context::SharedPtr
|
||||
get_context() const;
|
||||
|
||||
/// Return the underlying rcl guard condition structure.
|
||||
RCLCPP_PUBLIC
|
||||
rcl_guard_condition_t &
|
||||
get_rcl_guard_condition();
|
||||
|
||||
/// Return the underlying rcl guard condition structure.
|
||||
RCLCPP_PUBLIC
|
||||
const rcl_guard_condition_t &
|
||||
|
||||
@@ -29,7 +29,7 @@ class InitOptions
|
||||
{
|
||||
public:
|
||||
/// If true, the context will be shutdown on SIGINT by the signal handler (if it was installed).
|
||||
bool shutdown_on_sigint = true;
|
||||
bool shutdown_on_signal = true;
|
||||
|
||||
/// Constructor
|
||||
/**
|
||||
|
||||
35
rclcpp/include/rclcpp/is_ros_compatible_type.hpp
Normal file
35
rclcpp/include/rclcpp/is_ros_compatible_type.hpp
Normal 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_
|
||||
@@ -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)
|
||||
@@ -114,7 +117,9 @@ public:
|
||||
: pub_(std::move(other.pub_)),
|
||||
message_(std::move(other.message_)),
|
||||
message_allocator_(std::move(other.message_allocator_))
|
||||
{}
|
||||
{
|
||||
other.message_ = nullptr;
|
||||
}
|
||||
|
||||
/// Destructor of the LoanedMessage class.
|
||||
/**
|
||||
|
||||
@@ -64,9 +64,11 @@ public:
|
||||
virtual void clear_handles() = 0;
|
||||
virtual void remove_null_handles(rcl_wait_set_t * wait_set) = 0;
|
||||
|
||||
virtual void add_guard_condition(const rcl_guard_condition_t * guard_condition) = 0;
|
||||
virtual void
|
||||
add_guard_condition(const rclcpp::GuardCondition & guard_condition) = 0;
|
||||
|
||||
virtual void remove_guard_condition(const rcl_guard_condition_t * guard_condition) = 0;
|
||||
virtual void
|
||||
remove_guard_condition(const rclcpp::GuardCondition * guard_condition) = 0;
|
||||
|
||||
virtual void
|
||||
get_next_subscription(
|
||||
|
||||
@@ -61,7 +61,7 @@ public:
|
||||
message_allocator_ = std::make_shared<MessageAlloc>();
|
||||
serialized_message_allocator_ = std::make_shared<SerializedMessageAlloc>();
|
||||
buffer_allocator_ = std::make_shared<BufferAlloc>();
|
||||
rcutils_allocator_ = rclcpp::allocator::get_rcl_allocator(*buffer_allocator_.get());
|
||||
rcutils_allocator_ = allocator::get_rcl_allocator<char, BufferAlloc>(*buffer_allocator_.get());
|
||||
}
|
||||
|
||||
explicit MessageMemoryStrategy(std::shared_ptr<Alloc> allocator)
|
||||
@@ -69,7 +69,7 @@ public:
|
||||
message_allocator_ = std::make_shared<MessageAlloc>(*allocator.get());
|
||||
serialized_message_allocator_ = std::make_shared<SerializedMessageAlloc>(*allocator.get());
|
||||
buffer_allocator_ = std::make_shared<BufferAlloc>(*allocator.get());
|
||||
rcutils_allocator_ = rclcpp::allocator::get_rcl_allocator(*buffer_allocator_.get());
|
||||
rcutils_allocator_ = allocator::get_rcl_allocator<char, BufferAlloc>(*buffer_allocator_.get());
|
||||
}
|
||||
|
||||
virtual ~MessageMemoryStrategy() = default;
|
||||
|
||||
@@ -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>>
|
||||
|
||||
@@ -86,7 +86,6 @@ template<
|
||||
typename MessageT,
|
||||
typename CallbackT,
|
||||
typename AllocatorT,
|
||||
typename CallbackMessageT,
|
||||
typename SubscriptionT,
|
||||
typename MessageMemoryStrategyT>
|
||||
std::shared_ptr<SubscriptionT>
|
||||
@@ -221,12 +220,16 @@ Node::declare_parameter(
|
||||
// get advantage of parameter value template magic to get
|
||||
// the correct rclcpp::ParameterType from ParameterT
|
||||
rclcpp::ParameterValue value{ParameterT{}};
|
||||
return this->declare_parameter(
|
||||
name,
|
||||
value.get_type(),
|
||||
parameter_descriptor,
|
||||
ignore_override
|
||||
).get<ParameterT>();
|
||||
try {
|
||||
return this->declare_parameter(
|
||||
name,
|
||||
value.get_type(),
|
||||
parameter_descriptor,
|
||||
ignore_override
|
||||
).get<ParameterT>();
|
||||
} catch (const ParameterTypeException &) {
|
||||
throw exceptions::UninitializedStaticallyTypedParameterException(name);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename ParameterT>
|
||||
|
||||
@@ -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"
|
||||
@@ -31,7 +34,7 @@ namespace node_interfaces
|
||||
{
|
||||
|
||||
/// Implementation of the NodeBase part of the Node API.
|
||||
class NodeBase : public NodeBaseInterface
|
||||
class NodeBase : public NodeBaseInterface, public std::enable_shared_from_this<NodeBase>
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeBase)
|
||||
@@ -95,22 +98,25 @@ 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 &
|
||||
get_associated_with_executor_atomic() override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
rcl_guard_condition_t *
|
||||
rclcpp::GuardCondition &
|
||||
get_notify_guard_condition() override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::unique_lock<std::recursive_mutex>
|
||||
acquire_notify_guard_condition_lock() const override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
get_use_intra_process_default() const override;
|
||||
@@ -132,13 +138,14 @@ 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_;
|
||||
|
||||
/// Guard condition for notifying the Executor of changes to this node.
|
||||
mutable std::recursive_mutex notify_guard_condition_mutex_;
|
||||
rcl_guard_condition_t notify_guard_condition_ = rcl_get_zero_initialized_guard_condition();
|
||||
rclcpp::GuardCondition notify_guard_condition_;
|
||||
bool notify_guard_condition_is_valid_;
|
||||
};
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#include "rclcpp/callback_group.hpp"
|
||||
#include "rclcpp/context.hpp"
|
||||
#include "rclcpp/guard_condition.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
@@ -122,11 +123,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
|
||||
@@ -134,24 +143,17 @@ public:
|
||||
std::atomic_bool &
|
||||
get_associated_with_executor_atomic() = 0;
|
||||
|
||||
/// Return guard condition that should be notified when the internal node state changes.
|
||||
/// Return a guard condition that should be notified when the internal node state changes.
|
||||
/**
|
||||
* For example, this should be notified when a publisher is added or removed.
|
||||
*
|
||||
* \return the rcl_guard_condition_t if it is valid, else nullptr
|
||||
* \return the GuardCondition if it is valid, else thow runtime error
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
rcl_guard_condition_t *
|
||||
rclcpp::GuardCondition &
|
||||
get_notify_guard_condition() = 0;
|
||||
|
||||
/// Acquire and return a scoped lock that protects the notify guard condition.
|
||||
/** This should be used when triggering the notify guard condition. */
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
std::unique_lock<std::recursive_mutex>
|
||||
acquire_notify_guard_condition_lock() const = 0;
|
||||
|
||||
/// Return the default preference for using intra process communication.
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -70,10 +71,34 @@ public:
|
||||
const std::string & node_name,
|
||||
const std::string & namespace_) const override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::map<std::string, std::vector<std::string>>
|
||||
get_client_names_and_types_by_node(
|
||||
const std::string & node_name,
|
||||
const std::string & namespace_) const override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::map<std::string, std::vector<std::string>>
|
||||
get_publisher_names_and_types_by_node(
|
||||
const std::string & node_name,
|
||||
const std::string & namespace_,
|
||||
bool no_demangle = false) const override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::map<std::string, std::vector<std::string>>
|
||||
get_subscriber_names_and_types_by_node(
|
||||
const std::string & node_name,
|
||||
const std::string & namespace_,
|
||||
bool no_demangle = false) const override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<std::string>
|
||||
get_node_names() const override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<std::tuple<std::string, std::string, std::string>>
|
||||
get_node_names_with_enclaves() const override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<std::pair<std::string, std::string>>
|
||||
get_node_names_and_namespaces() const override;
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <chrono>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -146,7 +147,9 @@ public:
|
||||
/**
|
||||
* A topic is considered to exist when at least one publisher or subscriber
|
||||
* exists for it, whether they be local or remote to this process.
|
||||
* The returned names are the actual names used and do not have remap rules applied.
|
||||
* The returned names are the actual names of the topics, either announced by another nodes or by this one.
|
||||
* Attempting to create publishers or subscribers using names returned by this function may not
|
||||
* result in the desired topic name being used depending on the remap rules in use.
|
||||
*
|
||||
* \param[in] no_demangle if true, topic names and types are not demangled
|
||||
*/
|
||||
@@ -160,7 +163,9 @@ public:
|
||||
* A service is considered to exist when at least one service server or
|
||||
* service client exists for it, whether they be local or remote to this
|
||||
* process.
|
||||
* The returned names are the actual names used and do not have remap rules applied.
|
||||
* The returned names are the actual names of the services, either announced by another nodes or by this one.
|
||||
* Attempting to create clients or services using names returned by this function may not result in
|
||||
* the desired service name being used depending on the remap rules in use.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
@@ -170,7 +175,9 @@ public:
|
||||
/// Return a map of existing service names to list of service types for a specific node.
|
||||
/**
|
||||
* This function only considers services - not clients.
|
||||
* The returned names are the actual names used and do not have remap rules applied.
|
||||
* The returned names are the actual names after remap rules applied.
|
||||
* Attempting to create service clients using names returned by this function may not
|
||||
* result in the desired service name being used depending on the remap rules in use.
|
||||
*
|
||||
* \param[in] node_name name of the node
|
||||
* \param[in] namespace_ namespace of the node
|
||||
@@ -182,18 +189,85 @@ public:
|
||||
const std::string & node_name,
|
||||
const std::string & namespace_) const = 0;
|
||||
|
||||
/// Return a map of existing service names and types with a specific node.
|
||||
/**
|
||||
* This function only considers clients - not service servers.
|
||||
* The returned names are the actual names after remap rules applied.
|
||||
* Attempting to create service servers using names returned by this function may not
|
||||
* result in the desired service name being used depending on the remap rules in use.
|
||||
*
|
||||
* \param[in] node_name name of the node
|
||||
* \param[in] namespace_ namespace of the node
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
std::map<std::string, std::vector<std::string>>
|
||||
get_client_names_and_types_by_node(
|
||||
const std::string & node_name,
|
||||
const std::string & namespace_) const = 0;
|
||||
|
||||
/// Return a map of existing topic names to list of topic types for a specific node.
|
||||
/**
|
||||
* This function only considers publishers - not subscribers.
|
||||
* The returned names are the actual names after remap rules applied.
|
||||
* Attempting to create publishers or subscribers using names returned by this function may not
|
||||
* result in the desired topic name being used depending on the remap rules in use.
|
||||
*
|
||||
* \param[in] node_name name of the node
|
||||
* \param[in] namespace_ namespace of the node
|
||||
* \param[in] no_demangle if true, topic names and types are not demangled
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
std::map<std::string, std::vector<std::string>>
|
||||
get_publisher_names_and_types_by_node(
|
||||
const std::string & node_name,
|
||||
const std::string & namespace_,
|
||||
bool no_demangle = false) const = 0;
|
||||
|
||||
/// Return a map of existing topic names to list of topic types for a specific node.
|
||||
/**
|
||||
* This function only considers subscribers - not publishers.
|
||||
* The returned names are the actual names after remap rules applied.
|
||||
* Attempting to create publishers or subscribers using names returned by this function may not
|
||||
* result in the desired topic name being used depending on the remap rules in use.
|
||||
*
|
||||
* \param[in] node_name name of the node
|
||||
* \param[in] namespace_ namespace of the node
|
||||
* \param[in] no_demangle if true, topic names and types are not demangled
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
std::map<std::string, std::vector<std::string>>
|
||||
get_subscriber_names_and_types_by_node(
|
||||
const std::string & node_name,
|
||||
const std::string & namespace_,
|
||||
bool no_demangle = false) const = 0;
|
||||
|
||||
/// Return a vector of existing node names (string).
|
||||
/*
|
||||
* The returned names are the actual names used and do not have remap rules applied.
|
||||
* The returned names are the actual names after remap rules applied.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
std::vector<std::string>
|
||||
get_node_names() const = 0;
|
||||
|
||||
/// Return a vector of existing node names, namespaces and enclaves (tuple of string).
|
||||
/*
|
||||
* The returned names are the actual names after remap rules applied.
|
||||
* The enclaves contain the runtime security artifacts, those can be
|
||||
* used to establish secured network.
|
||||
* See https://design.ros2.org/articles/ros2_security_enclaves.html
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
std::vector<std::tuple<std::string, std::string, std::string>>
|
||||
get_node_names_with_enclaves() const = 0;
|
||||
|
||||
/// Return a vector of existing node names and namespaces (pair of string).
|
||||
/*
|
||||
* The returned names are the actual names used and do not have remap rules applied.
|
||||
* The returned names are the actual names after remap rules applied.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
|
||||
@@ -48,7 +48,7 @@ public:
|
||||
rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging,
|
||||
rclcpp::node_interfaces::NodeClockInterface::SharedPtr node_clock,
|
||||
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters,
|
||||
const rclcpp::QoS & qos = rclcpp::RosoutQoS(),
|
||||
const rclcpp::QoS & qos = rclcpp::ClockQoS(),
|
||||
bool use_clock_thread = true
|
||||
);
|
||||
|
||||
|
||||
@@ -349,6 +349,9 @@ public:
|
||||
* global arguments (e.g. parameter overrides from a YAML file), which are
|
||||
* not explicitly declared will not appear on the node at all, even if
|
||||
* `allow_undeclared_parameters` is true.
|
||||
* Parameter declaration from overrides is done in the node's base constructor,
|
||||
* so the user must take care to check if the parameter is already (e.g.
|
||||
* automatically) declared before declaring it themselves.
|
||||
* Already declared parameters will not be re-declared, and parameters
|
||||
* declared in this way will use the default constructed ParameterDescriptor.
|
||||
*/
|
||||
|
||||
@@ -169,13 +169,17 @@ public:
|
||||
NodeT node,
|
||||
const rclcpp::QoS & qos =
|
||||
rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(rmw_qos_profile_parameter_events)))
|
||||
: node_base_(rclcpp::node_interfaces::get_node_base_interface(node))
|
||||
{
|
||||
node_base_ = rclcpp::node_interfaces::get_node_base_interface(node);
|
||||
auto node_topics = rclcpp::node_interfaces::get_node_topics_interface(node);
|
||||
|
||||
callbacks_ = std::make_shared<Callbacks>();
|
||||
|
||||
event_subscription_ = rclcpp::create_subscription<rcl_interfaces::msg::ParameterEvent>(
|
||||
node_topics, "/parameter_events", qos,
|
||||
std::bind(&ParameterEventHandler::event_callback, this, std::placeholders::_1));
|
||||
[callbacks = callbacks_](const rcl_interfaces::msg::ParameterEvent & event) {
|
||||
callbacks->event_callback(event);
|
||||
});
|
||||
}
|
||||
|
||||
using ParameterEventCallbackType =
|
||||
@@ -249,8 +253,8 @@ public:
|
||||
get_parameter_from_event(
|
||||
const rcl_interfaces::msg::ParameterEvent & event,
|
||||
rclcpp::Parameter & parameter,
|
||||
const std::string parameter_name,
|
||||
const std::string node_name = "");
|
||||
const std::string & parameter_name,
|
||||
const std::string & node_name = "");
|
||||
|
||||
/// Get an rclcpp::Parameter from parameter event
|
||||
/**
|
||||
@@ -269,8 +273,8 @@ public:
|
||||
static rclcpp::Parameter
|
||||
get_parameter_from_event(
|
||||
const rcl_interfaces::msg::ParameterEvent & event,
|
||||
const std::string parameter_name,
|
||||
const std::string node_name = "");
|
||||
const std::string & parameter_name,
|
||||
const std::string & node_name = "");
|
||||
|
||||
/// Get all rclcpp::Parameter values from a parameter event
|
||||
/**
|
||||
@@ -285,17 +289,6 @@ public:
|
||||
using CallbacksContainerType = std::list<ParameterCallbackHandle::WeakPtr>;
|
||||
|
||||
protected:
|
||||
/// Callback for parameter events subscriptions.
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
event_callback(const rcl_interfaces::msg::ParameterEvent & event);
|
||||
|
||||
// Utility function for resolving node path.
|
||||
std::string resolve_path(const std::string & path);
|
||||
|
||||
// Node interface used for base functionality
|
||||
std::shared_ptr<rclcpp::node_interfaces::NodeBaseInterface> node_base_;
|
||||
|
||||
// *INDENT-OFF* Uncrustify doesn't handle indented public/private labels
|
||||
// Hash function for string pair required in std::unordered_map
|
||||
// See: https://stackoverflow.com/questions/35985960/c-why-is-boosthash-combine-the-best-way-to-combine-hash-values
|
||||
@@ -319,18 +312,34 @@ protected:
|
||||
};
|
||||
// *INDENT-ON*
|
||||
|
||||
// Map container for registered parameters
|
||||
std::unordered_map<
|
||||
std::pair<std::string, std::string>,
|
||||
CallbacksContainerType,
|
||||
StringPairHash
|
||||
> parameter_callbacks_;
|
||||
struct Callbacks
|
||||
{
|
||||
std::recursive_mutex mutex_;
|
||||
|
||||
// Map container for registered parameters
|
||||
std::unordered_map<
|
||||
std::pair<std::string, std::string>,
|
||||
CallbacksContainerType,
|
||||
StringPairHash
|
||||
> parameter_callbacks_;
|
||||
|
||||
std::list<ParameterEventCallbackHandle::WeakPtr> event_callbacks_;
|
||||
|
||||
/// Callback for parameter events subscriptions.
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
event_callback(const rcl_interfaces::msg::ParameterEvent & event);
|
||||
};
|
||||
|
||||
std::shared_ptr<Callbacks> callbacks_;
|
||||
|
||||
// Utility function for resolving node path.
|
||||
std::string resolve_path(const std::string & path);
|
||||
|
||||
// Node interface used for base functionality
|
||||
std::shared_ptr<rclcpp::node_interfaces::NodeBaseInterface> node_base_;
|
||||
|
||||
rclcpp::Subscription<rcl_interfaces::msg::ParameterEvent>::SharedPtr event_subscription_;
|
||||
|
||||
std::list<ParameterEventCallbackHandle::WeakPtr> event_callbacks_;
|
||||
|
||||
std::recursive_mutex mutex_;
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
@@ -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<
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <rmw/error_handling.h>
|
||||
#include <rmw/rmw.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
@@ -33,6 +34,7 @@
|
||||
#include "rclcpp/qos_event.hpp"
|
||||
#include "rclcpp/type_support_decl.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
#include "rcpputils/time.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
@@ -203,6 +205,46 @@ public:
|
||||
std::vector<rclcpp::NetworkFlowEndpoint>
|
||||
get_network_flow_endpoints() const;
|
||||
|
||||
/// Wait until all published messages are acknowledged or until the specified timeout elapses.
|
||||
/**
|
||||
* This method waits until all published messages are acknowledged by all matching
|
||||
* subscriptions or the given timeout elapses.
|
||||
*
|
||||
* If the timeout is negative then this method will block indefinitely until all published
|
||||
* messages are acknowledged.
|
||||
* If the timeout is zero then this method will not block, it will check if all published
|
||||
* messages are acknowledged and return immediately.
|
||||
* If the timeout is greater than zero, this method will wait until all published messages are
|
||||
* acknowledged or the timeout elapses.
|
||||
*
|
||||
* This method only waits for acknowledgments if the publisher's QoS profile is RELIABLE.
|
||||
* Otherwise this method will immediately return `true`.
|
||||
*
|
||||
* \param[in] timeout the duration to wait for all published messages to be acknowledged.
|
||||
* \return `true` if all published messages were acknowledged before the given timeout
|
||||
* elapsed, otherwise `false`.
|
||||
* \throws rclcpp::exceptions::RCLError if middleware doesn't support or internal error occurs
|
||||
* \throws std::invalid_argument if timeout is greater than std::chrono::nanoseconds::max() or
|
||||
* less than std::chrono::nanoseconds::min()
|
||||
*/
|
||||
template<typename DurationRepT = int64_t, typename DurationT = std::milli>
|
||||
bool
|
||||
wait_for_all_acked(
|
||||
std::chrono::duration<DurationRepT, DurationT> timeout =
|
||||
std::chrono::duration<DurationRepT, DurationT>(-1)) const
|
||||
{
|
||||
rcl_duration_value_t rcl_timeout = rcpputils::convert_to_nanoseconds(timeout).count();
|
||||
|
||||
rcl_ret_t ret = rcl_publisher_wait_for_all_acked(publisher_handle_.get(), rcl_timeout);
|
||||
if (ret == RCL_RET_OK) {
|
||||
return true;
|
||||
} else if (ret == RCL_RET_TIMEOUT) {
|
||||
return false;
|
||||
} else {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
template<typename EventCallbackT>
|
||||
void
|
||||
|
||||
@@ -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,7 +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();
|
||||
result.allocator = rclcpp::allocator::get_rcl_allocator(*this->get_allocator());
|
||||
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;
|
||||
@@ -99,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>>;
|
||||
|
||||
@@ -94,7 +94,7 @@ public:
|
||||
|
||||
/// Add the Waitable to a wait set.
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
void
|
||||
add_to_wait_set(rcl_wait_set_t * wait_set) override;
|
||||
|
||||
/// Check if the Waitable is ready.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "rcl/allocator.h"
|
||||
|
||||
#include "rclcpp/allocator/allocator_common.hpp"
|
||||
#include "rclcpp/detail/add_guard_condition_to_rcl_wait_set.hpp"
|
||||
#include "rclcpp/memory_strategy.hpp"
|
||||
#include "rclcpp/node.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
@@ -61,17 +62,17 @@ public:
|
||||
allocator_ = std::make_shared<VoidAlloc>();
|
||||
}
|
||||
|
||||
void add_guard_condition(const rcl_guard_condition_t * guard_condition) override
|
||||
void add_guard_condition(const rclcpp::GuardCondition & guard_condition) override
|
||||
{
|
||||
for (const auto & existing_guard_condition : guard_conditions_) {
|
||||
if (existing_guard_condition == guard_condition) {
|
||||
if (existing_guard_condition == &guard_condition) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
guard_conditions_.push_back(guard_condition);
|
||||
guard_conditions_.push_back(&guard_condition);
|
||||
}
|
||||
|
||||
void remove_guard_condition(const rcl_guard_condition_t * guard_condition) override
|
||||
void remove_guard_condition(const rclcpp::GuardCondition * guard_condition) override
|
||||
{
|
||||
for (auto it = guard_conditions_.begin(); it != guard_conditions_.end(); ++it) {
|
||||
if (*it == guard_condition) {
|
||||
@@ -240,22 +241,11 @@ public:
|
||||
}
|
||||
|
||||
for (auto guard_condition : guard_conditions_) {
|
||||
if (rcl_wait_set_add_guard_condition(wait_set, guard_condition, NULL) != RCL_RET_OK) {
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
"rclcpp",
|
||||
"Couldn't add guard_condition to wait set: %s",
|
||||
rcl_get_error_string().str);
|
||||
return false;
|
||||
}
|
||||
detail::add_guard_condition_to_rcl_wait_set(*wait_set, *guard_condition);
|
||||
}
|
||||
|
||||
for (auto waitable : waitable_handles_) {
|
||||
if (!waitable->add_to_wait_set(wait_set)) {
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
"rclcpp",
|
||||
"Couldn't add waitable to wait set: %s", rcl_get_error_string().str);
|
||||
return false;
|
||||
}
|
||||
waitable->add_to_wait_set(wait_set);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -388,6 +378,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 +390,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);
|
||||
}
|
||||
}
|
||||
@@ -437,7 +432,7 @@ public:
|
||||
|
||||
rcl_allocator_t get_allocator() override
|
||||
{
|
||||
return rclcpp::allocator::get_rcl_allocator(*allocator_.get());
|
||||
return rclcpp::allocator::get_rcl_allocator<void *, VoidAlloc>(*allocator_.get());
|
||||
}
|
||||
|
||||
size_t number_of_ready_subscriptions() const override
|
||||
@@ -504,7 +499,7 @@ private:
|
||||
using VectorRebind =
|
||||
std::vector<T, typename std::allocator_traits<Alloc>::template rebind_alloc<T>>;
|
||||
|
||||
VectorRebind<const rcl_guard_condition_t *> guard_conditions_;
|
||||
VectorRebind<const rclcpp::GuardCondition *> guard_conditions_;
|
||||
|
||||
VectorRebind<std::shared_ptr<const rcl_subscription_t>> subscription_handles_;
|
||||
VectorRebind<std::shared_ptr<const rcl_service_t>> service_handles_;
|
||||
|
||||
@@ -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,25 +140,25 @@ 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)
|
||||
{
|
||||
if (options.event_callbacks.deadline_callback) {
|
||||
if (options_.event_callbacks.deadline_callback) {
|
||||
this->add_event_handler(
|
||||
options.event_callbacks.deadline_callback,
|
||||
options_.event_callbacks.deadline_callback,
|
||||
RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED);
|
||||
}
|
||||
if (options.event_callbacks.liveliness_callback) {
|
||||
if (options_.event_callbacks.liveliness_callback) {
|
||||
this->add_event_handler(
|
||||
options.event_callbacks.liveliness_callback,
|
||||
options_.event_callbacks.liveliness_callback,
|
||||
RCL_SUBSCRIPTION_LIVELINESS_CHANGED);
|
||||
}
|
||||
if (options.event_callbacks.incompatible_qos_callback) {
|
||||
if (options_.event_callbacks.incompatible_qos_callback) {
|
||||
this->add_event_handler(
|
||||
options.event_callbacks.incompatible_qos_callback,
|
||||
options_.event_callbacks.incompatible_qos_callback,
|
||||
RCL_SUBSCRIPTION_REQUESTED_INCOMPATIBLE_QOS);
|
||||
} else if (options_.use_default_callbacks) {
|
||||
// Register default callback when not specified
|
||||
@@ -144,27 +172,27 @@ public:
|
||||
// pass
|
||||
}
|
||||
}
|
||||
if (options.event_callbacks.message_lost_callback) {
|
||||
if (options_.event_callbacks.message_lost_callback) {
|
||||
this->add_event_handler(
|
||||
options.event_callbacks.message_lost_callback,
|
||||
options_.event_callbacks.message_lost_callback,
|
||||
RCL_SUBSCRIPTION_MESSAGE_LOST);
|
||||
}
|
||||
|
||||
// Setup intra process publishing if requested.
|
||||
if (rclcpp::detail::resolve_use_intra_process(options, *node_base)) {
|
||||
if (rclcpp::detail::resolve_use_intra_process(options_, *node_base)) {
|
||||
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");
|
||||
}
|
||||
@@ -173,11 +201,11 @@ public:
|
||||
auto context = node_base->get_context();
|
||||
subscription_intra_process_ = std::make_shared<SubscriptionIntraProcessT>(
|
||||
callback,
|
||||
options.get_allocator(),
|
||||
options_.get_allocator(),
|
||||
context,
|
||||
this->get_topic_name(), // important to get like this, as it has the fully-qualified name
|
||||
qos_profile,
|
||||
resolve_intra_process_buffer_type(options.intra_process_buffer_type, callback));
|
||||
resolve_intra_process_buffer_type(options_.intra_process_buffer_type, callback));
|
||||
TRACEPOINT(
|
||||
rclcpp_subscription_init,
|
||||
static_cast<const void *>(get_subscription_handle().get()),
|
||||
@@ -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_;
|
||||
};
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ public:
|
||||
/// Get the actual QoS settings, after the defaults have been determined.
|
||||
/**
|
||||
* The actual configuration applied when using RMW_QOS_POLICY_*_SYSTEM_DEFAULT
|
||||
* can only be resolved after the creation of the publisher, and it
|
||||
* can only be resolved after the creation of the subscription, and it
|
||||
* depends on the underlying rmw implementation.
|
||||
* If the underlying setting in use can't be represented in ROS terms,
|
||||
* it will be set to RMW_QOS_POLICY_*_UNKNOWN.
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,7 +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();
|
||||
result.allocator = rclcpp::allocator::get_rcl_allocator(*this->get_allocator());
|
||||
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 =
|
||||
@@ -121,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>>;
|
||||
|
||||
@@ -74,14 +74,22 @@ public:
|
||||
const rclcpp::QoS & qos = rclcpp::ClockQoS(),
|
||||
bool use_clock_thread = true);
|
||||
|
||||
/// Attack node to the time source.
|
||||
// The TimeSource is uncopyable
|
||||
TimeSource(const TimeSource &) = delete;
|
||||
TimeSource & operator=(const TimeSource &) = delete;
|
||||
|
||||
// The TimeSource is moveable
|
||||
TimeSource(TimeSource &&) = default;
|
||||
TimeSource & operator=(TimeSource &&) = default;
|
||||
|
||||
/// 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.
|
||||
@@ -116,89 +124,36 @@ public:
|
||||
RCLCPP_PUBLIC
|
||||
void attachClock(rclcpp::Clock::SharedPtr clock);
|
||||
|
||||
/// Detach a clock to the time source
|
||||
/// Detach a clock from the time source
|
||||
RCLCPP_PUBLIC
|
||||
void detachClock(rclcpp::Clock::SharedPtr clock);
|
||||
|
||||
/// Get whether a separate clock thread is used or not
|
||||
RCLCPP_PUBLIC
|
||||
bool get_use_clock_thread();
|
||||
|
||||
/// Set whether to use a separate clock thread or not
|
||||
RCLCPP_PUBLIC
|
||||
void set_use_clock_thread(bool use_clock_thread);
|
||||
|
||||
/// Check if the clock thread is joinable
|
||||
RCLCPP_PUBLIC
|
||||
bool clock_thread_is_joinable();
|
||||
|
||||
/// TimeSource Destructor
|
||||
RCLCPP_PUBLIC
|
||||
~TimeSource();
|
||||
|
||||
protected:
|
||||
// Dedicated thread for clock subscription.
|
||||
bool use_clock_thread_;
|
||||
std::thread clock_executor_thread_;
|
||||
|
||||
private:
|
||||
// Preserve the node reference
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base_;
|
||||
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics_;
|
||||
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph_;
|
||||
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services_;
|
||||
rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging_;
|
||||
rclcpp::node_interfaces::NodeClockInterface::SharedPtr node_clock_;
|
||||
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters_;
|
||||
class ClocksState;
|
||||
std::shared_ptr<ClocksState> clocks_state_;
|
||||
|
||||
// Store (and update on node attach) logger for logging.
|
||||
Logger logger_;
|
||||
class NodeState;
|
||||
std::shared_ptr<NodeState> node_state_;
|
||||
|
||||
// QoS of the clock subscription.
|
||||
rclcpp::QoS qos_;
|
||||
|
||||
// The subscription for the clock callback
|
||||
using MessageT = rosgraph_msgs::msg::Clock;
|
||||
using Alloc = std::allocator<void>;
|
||||
using SubscriptionT = rclcpp::Subscription<MessageT, Alloc>;
|
||||
std::shared_ptr<SubscriptionT> clock_subscription_{nullptr};
|
||||
std::mutex clock_sub_lock_;
|
||||
rclcpp::CallbackGroup::SharedPtr clock_callback_group_;
|
||||
rclcpp::executors::SingleThreadedExecutor::SharedPtr clock_executor_;
|
||||
std::promise<void> cancel_clock_executor_promise_;
|
||||
|
||||
// The clock callback itself
|
||||
void clock_cb(std::shared_ptr<const rosgraph_msgs::msg::Clock> msg);
|
||||
|
||||
// Create the subscription for the clock topic
|
||||
void create_clock_sub();
|
||||
|
||||
// Destroy the subscription for the clock topic
|
||||
void destroy_clock_sub();
|
||||
|
||||
// Parameter Event subscription
|
||||
using ParamMessageT = rcl_interfaces::msg::ParameterEvent;
|
||||
using ParamSubscriptionT = rclcpp::Subscription<ParamMessageT, Alloc>;
|
||||
std::shared_ptr<ParamSubscriptionT> parameter_subscription_;
|
||||
|
||||
// Callback for parameter updates
|
||||
void on_parameter_event(std::shared_ptr<const rcl_interfaces::msg::ParameterEvent> event);
|
||||
|
||||
// An enum to hold the parameter state
|
||||
enum UseSimTimeParameterState {UNSET, SET_TRUE, SET_FALSE};
|
||||
UseSimTimeParameterState parameter_state_;
|
||||
|
||||
// An internal method to use in the clock callback that iterates and enables all clocks
|
||||
void enable_ros_time();
|
||||
// An internal method to use in the clock callback that iterates and disables all clocks
|
||||
void disable_ros_time();
|
||||
|
||||
// Internal helper functions used inside iterators
|
||||
static void set_clock(
|
||||
const builtin_interfaces::msg::Time::SharedPtr msg,
|
||||
bool set_ros_time_enabled,
|
||||
rclcpp::Clock::SharedPtr clock);
|
||||
|
||||
// Local storage of validity of ROS time
|
||||
// This is needed when new clocks are added.
|
||||
bool ros_time_active_{false};
|
||||
// Last set message to be passed to newly registered clocks
|
||||
std::shared_ptr<const rosgraph_msgs::msg::Clock> last_msg_set_;
|
||||
|
||||
// A lock to protect iterating the associated_clocks_ field.
|
||||
std::mutex clock_list_lock_;
|
||||
// A vector to store references to associated clocks.
|
||||
std::vector<rclcpp::Clock::SharedPtr> associated_clocks_;
|
||||
// A handler for the use_sim_time parameter callback.
|
||||
rclcpp::node_interfaces::OnSetParametersCallbackHandle::SharedPtr sim_time_cb_handler_{nullptr};
|
||||
// Preserve the arguments received by the constructor for reuse at runtime
|
||||
bool constructed_use_clock_thread_;
|
||||
rclcpp::QoS constructed_qos_;
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
@@ -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_));
|
||||
|
||||
201
rclcpp/include/rclcpp/type_adapter.hpp
Normal file
201
rclcpp/include/rclcpp/type_adapter.hpp
Normal 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_
|
||||
@@ -42,6 +42,20 @@ std::string to_string(T value)
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
/// Option to indicate which signal handlers rclcpp should install.
|
||||
enum class SignalHandlerOptions
|
||||
{
|
||||
/// Install both sigint and sigterm, this is the default behavior.
|
||||
All,
|
||||
/// Install only a sigint handler.
|
||||
SigInt,
|
||||
/// Install only a sigterm handler.
|
||||
SigTerm,
|
||||
/// Do not install any signal handler.
|
||||
None,
|
||||
};
|
||||
|
||||
/// Initialize communications via the rmw implementation and set up a global signal handler.
|
||||
/**
|
||||
* Initializes the global context which is accessible via the function
|
||||
@@ -50,10 +64,16 @@ namespace rclcpp
|
||||
* rclcpp::install_signal_handlers().
|
||||
*
|
||||
* \sa rclcpp::Context::init() for more details on arguments and possible exceptions
|
||||
*
|
||||
* \param signal_handler_options option to indicate which signal handlers should be installed.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
init(int argc, char const * const argv[], const InitOptions & init_options = InitOptions());
|
||||
init(
|
||||
int argc,
|
||||
char const * const argv[],
|
||||
const InitOptions & init_options = InitOptions(),
|
||||
SignalHandlerOptions signal_handler_options = SignalHandlerOptions::All);
|
||||
|
||||
/// Install the global signal handler for rclcpp.
|
||||
/**
|
||||
@@ -67,17 +87,26 @@ init(int argc, char const * const argv[], const InitOptions & init_options = Ini
|
||||
*
|
||||
* This function is thread-safe.
|
||||
*
|
||||
* \param signal_handler_options option to indicate which signal handlers should be installed.
|
||||
* \return true if signal handler was installed by this function, false if already installed.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
install_signal_handlers();
|
||||
install_signal_handlers(SignalHandlerOptions signal_handler_options = SignalHandlerOptions::All);
|
||||
|
||||
/// Return true if the signal handlers are installed, otherwise false.
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
signal_handlers_installed();
|
||||
|
||||
/// Get the current signal handler options.
|
||||
/**
|
||||
* If no signal handler is installed, SignalHandlerOptions::None is returned.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
SignalHandlerOptions
|
||||
get_current_signal_handler_options();
|
||||
|
||||
/// Uninstall the global signal handler for rclcpp.
|
||||
/**
|
||||
* This function does not necessarily need to be called, but can be used to
|
||||
|
||||
100
rclcpp/include/rclcpp/wait_for_message.hpp
Normal file
100
rclcpp/include/rclcpp/wait_for_message.hpp
Normal 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<const 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_
|
||||
@@ -383,10 +383,7 @@ protected:
|
||||
continue;
|
||||
}
|
||||
rclcpp::Waitable & waitable = *waitable_ptr_pair.second;
|
||||
bool successful = waitable.add_to_wait_set(&rcl_wait_set_);
|
||||
if (!successful) {
|
||||
throw std::runtime_error("waitable unexpectedly failed to be added to wait set");
|
||||
}
|
||||
waitable.add_to_wait_set(&rcl_wait_set_);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -203,7 +203,7 @@ protected:
|
||||
std::shared_ptr<rclcpp::Waitable> && waitable,
|
||||
std::shared_ptr<void> && associated_entity,
|
||||
std::function<
|
||||
void(std::shared_ptr<rclcpp::Waitable>&&, std::shared_ptr<void>&&)
|
||||
void(std::shared_ptr<rclcpp::Waitable>&&, std::shared_ptr<void> &&)
|
||||
> add_waitable_function)
|
||||
{
|
||||
// Explicitly no thread synchronization.
|
||||
|
||||
@@ -216,7 +216,7 @@ protected:
|
||||
std::shared_ptr<rclcpp::Waitable> && waitable,
|
||||
std::shared_ptr<void> && associated_entity,
|
||||
std::function<
|
||||
void(std::shared_ptr<rclcpp::Waitable>&&, std::shared_ptr<void>&&)
|
||||
void(std::shared_ptr<rclcpp::Waitable>&&, std::shared_ptr<void> &&)
|
||||
> add_waitable_function)
|
||||
{
|
||||
using rclcpp::wait_set_policies::detail::WritePreferringReadWriteLock;
|
||||
|
||||
@@ -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>>(
|
||||
|
||||
@@ -103,12 +103,11 @@ public:
|
||||
/// Add the Waitable to a wait set.
|
||||
/**
|
||||
* \param[in] wait_set A handle to the wait set to add the Waitable to.
|
||||
* \return `true` if the Waitable is added successfully, `false` otherwise.
|
||||
* \throws rclcpp::execptions::RCLError from rcl_wait_set_add_*()
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
bool
|
||||
void
|
||||
add_to_wait_set(rcl_wait_set_t * wait_set) = 0;
|
||||
|
||||
/// Check if the Waitable is ready.
|
||||
@@ -145,8 +144,7 @@ public:
|
||||
* ```cpp
|
||||
* // ... create a wait set and a Waitable
|
||||
* // Add the Waitable to the wait set
|
||||
* bool add_ret = waitable.add_to_wait_set(wait_set);
|
||||
* // ... error handling
|
||||
* waitable.add_to_wait_set(wait_set);
|
||||
* // Wait
|
||||
* rcl_ret_t wait_ret = rcl_wait(wait_set);
|
||||
* // ... error handling
|
||||
@@ -172,8 +170,7 @@ public:
|
||||
* ```cpp
|
||||
* // ... create a wait set and a Waitable
|
||||
* // Add the Waitable to the wait set
|
||||
* bool add_ret = waitable.add_to_wait_set(wait_set);
|
||||
* // ... error handling
|
||||
* waitable.add_to_wait_set(wait_set);
|
||||
* // Wait
|
||||
* rcl_ret_t wait_ret = rcl_wait(wait_set);
|
||||
* // ... error handling
|
||||
|
||||
@@ -2,15 +2,17 @@
|
||||
<?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>14.1.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>
|
||||
<maintainer email="jacob@openrobotics.org">Jacob Perron</maintainer>
|
||||
<maintainer email="william@openrobotics.org">William Woodall</maintainer>
|
||||
<license>Apache License 2.0</license>
|
||||
<author email="dthomas@openrobotics.org">Dirk Thomas</author>
|
||||
|
||||
<buildtool_depend>ament_cmake_ros</buildtool_depend>
|
||||
<buildtool_depend>ament_cmake_gen_version_h</buildtool_depend>
|
||||
<buildtool_depend>python3</buildtool_depend>
|
||||
|
||||
<build_depend>ament_index_cpp</build_depend>
|
||||
<build_depend>builtin_interfaces</build_depend>
|
||||
|
||||
@@ -149,7 +149,7 @@ def get_rclcpp_suffix_from_features(features):
|
||||
@[ if params]@
|
||||
@(''.join([' ' + p + ', \\\n' for p in params if p != stream_arg]))@
|
||||
@[ end if]@
|
||||
logger.get_name(), \
|
||||
(logger).get_name(), \
|
||||
@[ if 'stream' not in feature_combination]@
|
||||
__VA_ARGS__); \
|
||||
@[ else]@
|
||||
|
||||
@@ -14,11 +14,14 @@
|
||||
|
||||
#include "rclcpp/clock.hpp"
|
||||
|
||||
#include <condition_variable>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/utilities.hpp"
|
||||
|
||||
#include "rcpputils/scope_exit.hpp"
|
||||
#include "rcutils/logging_macros.h"
|
||||
|
||||
namespace rclcpp
|
||||
@@ -76,6 +79,109 @@ Clock::now()
|
||||
return now;
|
||||
}
|
||||
|
||||
bool
|
||||
Clock::sleep_until(Time until, Context::SharedPtr context)
|
||||
{
|
||||
if (!context || !context->is_valid()) {
|
||||
throw std::runtime_error("context cannot be slept with because it's invalid");
|
||||
}
|
||||
const auto this_clock_type = get_clock_type();
|
||||
if (until.get_clock_type() != this_clock_type) {
|
||||
throw std::runtime_error("until's clock type does not match this clock's type");
|
||||
}
|
||||
bool time_source_changed = false;
|
||||
|
||||
std::condition_variable cv;
|
||||
|
||||
// Wake this thread if the context is shutdown
|
||||
rclcpp::OnShutdownCallbackHandle shutdown_cb_handle = context->add_on_shutdown_callback(
|
||||
[&cv]() {
|
||||
cv.notify_one();
|
||||
});
|
||||
// No longer need the shutdown callback when this function exits
|
||||
auto callback_remover = rcpputils::scope_exit(
|
||||
[context, &shutdown_cb_handle]() {
|
||||
context->remove_on_shutdown_callback(shutdown_cb_handle);
|
||||
});
|
||||
|
||||
if (this_clock_type == RCL_STEADY_TIME) {
|
||||
// Synchronize because RCL steady clock epoch might differ from chrono::steady_clock epoch
|
||||
const Time rcl_entry = now();
|
||||
const std::chrono::steady_clock::time_point chrono_entry = std::chrono::steady_clock::now();
|
||||
const Duration delta_t = until - rcl_entry;
|
||||
const std::chrono::steady_clock::time_point chrono_until =
|
||||
chrono_entry + std::chrono::nanoseconds(delta_t.nanoseconds());
|
||||
|
||||
// loop over spurious wakeups but notice shutdown
|
||||
std::unique_lock lock(impl_->clock_mutex_);
|
||||
while (now() < until && context->is_valid()) {
|
||||
cv.wait_until(lock, chrono_until);
|
||||
}
|
||||
} else if (this_clock_type == RCL_SYSTEM_TIME) {
|
||||
auto system_time = std::chrono::system_clock::time_point(
|
||||
// Cast because system clock resolution is too big for nanoseconds on some systems
|
||||
std::chrono::duration_cast<std::chrono::system_clock::duration>(
|
||||
std::chrono::nanoseconds(until.nanoseconds())));
|
||||
|
||||
// loop over spurious wakeups but notice shutdown
|
||||
std::unique_lock lock(impl_->clock_mutex_);
|
||||
while (now() < until && context->is_valid()) {
|
||||
cv.wait_until(lock, system_time);
|
||||
}
|
||||
} else if (this_clock_type == RCL_ROS_TIME) {
|
||||
// Install jump handler for any amount of time change, for two purposes:
|
||||
// - if ROS time is active, check if time reached on each new clock sample
|
||||
// - Trigger via on_clock_change to detect if time source changes, to invalidate sleep
|
||||
rcl_jump_threshold_t threshold;
|
||||
threshold.on_clock_change = true;
|
||||
// 0 is disable, so -1 and 1 are smallest possible time changes
|
||||
threshold.min_backward.nanoseconds = -1;
|
||||
threshold.min_forward.nanoseconds = 1;
|
||||
auto clock_handler = create_jump_callback(
|
||||
nullptr,
|
||||
[&cv, &time_source_changed](const rcl_time_jump_t & jump) {
|
||||
if (jump.clock_change != RCL_ROS_TIME_NO_CHANGE) {
|
||||
time_source_changed = true;
|
||||
}
|
||||
cv.notify_one();
|
||||
},
|
||||
threshold);
|
||||
|
||||
if (!ros_time_is_active()) {
|
||||
auto system_time = std::chrono::system_clock::time_point(
|
||||
// Cast because system clock resolution is too big for nanoseconds on some systems
|
||||
std::chrono::duration_cast<std::chrono::system_clock::duration>(
|
||||
std::chrono::nanoseconds(until.nanoseconds())));
|
||||
|
||||
// loop over spurious wakeups but notice shutdown or time source change
|
||||
std::unique_lock lock(impl_->clock_mutex_);
|
||||
while (now() < until && context->is_valid() && !time_source_changed) {
|
||||
cv.wait_until(lock, system_time);
|
||||
}
|
||||
} else {
|
||||
// RCL_ROS_TIME with ros_time_is_active.
|
||||
// Just wait without "until" because installed
|
||||
// jump callbacks wake the cv on every new sample.
|
||||
std::unique_lock lock(impl_->clock_mutex_);
|
||||
while (now() < until && context->is_valid() && !time_source_changed) {
|
||||
cv.wait(lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!context->is_valid() || time_source_changed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return now() >= until;
|
||||
}
|
||||
|
||||
bool
|
||||
Clock::sleep_for(Duration rel_time, Context::SharedPtr context)
|
||||
{
|
||||
return sleep_until(now() + rel_time, context);
|
||||
}
|
||||
|
||||
bool
|
||||
Clock::ros_time_is_active()
|
||||
{
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
|
||||
#include "rcl/init.h"
|
||||
@@ -307,6 +308,15 @@ Context::shutdown(const std::string & reason)
|
||||
// if it is not valid, then it cannot be shutdown
|
||||
return false;
|
||||
}
|
||||
|
||||
// call each pre-shutdown callback
|
||||
{
|
||||
std::lock_guard<std::mutex> lock{pre_shutdown_callbacks_mutex_};
|
||||
for (const auto & callback : pre_shutdown_callbacks_) {
|
||||
(*callback)();
|
||||
}
|
||||
}
|
||||
|
||||
// rcl shutdown
|
||||
rcl_ret_t ret = rcl_shutdown(rcl_context_.get());
|
||||
if (RCL_RET_OK != ret) {
|
||||
@@ -315,9 +325,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 +358,130 @@ 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_;
|
||||
return add_shutdown_callback(ShutdownType::on_shutdown, callback);
|
||||
}
|
||||
|
||||
std::vector<rclcpp::Context::OnShutdownCallback> &
|
||||
Context::get_on_shutdown_callbacks()
|
||||
bool
|
||||
Context::remove_on_shutdown_callback(const OnShutdownCallbackHandle & callback_handle)
|
||||
{
|
||||
return on_shutdown_callbacks_;
|
||||
return remove_shutdown_callback(ShutdownType::on_shutdown, callback_handle);
|
||||
}
|
||||
|
||||
rclcpp::PreShutdownCallbackHandle
|
||||
Context::add_pre_shutdown_callback(PreShutdownCallback callback)
|
||||
{
|
||||
return add_shutdown_callback(ShutdownType::pre_shutdown, callback);
|
||||
}
|
||||
|
||||
bool
|
||||
Context::remove_pre_shutdown_callback(
|
||||
const PreShutdownCallbackHandle & callback_handle)
|
||||
{
|
||||
return remove_shutdown_callback(ShutdownType::pre_shutdown, callback_handle);
|
||||
}
|
||||
|
||||
rclcpp::ShutdownCallbackHandle
|
||||
Context::add_shutdown_callback(
|
||||
ShutdownType shutdown_type,
|
||||
ShutdownCallback callback)
|
||||
{
|
||||
auto callback_shared_ptr =
|
||||
std::make_shared<ShutdownCallbackHandle::ShutdownCallbackType>(callback);
|
||||
|
||||
switch (shutdown_type) {
|
||||
case ShutdownType::pre_shutdown:
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(pre_shutdown_callbacks_mutex_);
|
||||
pre_shutdown_callbacks_.emplace(callback_shared_ptr);
|
||||
}
|
||||
break;
|
||||
case ShutdownType::on_shutdown:
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(on_shutdown_callbacks_mutex_);
|
||||
on_shutdown_callbacks_.emplace(callback_shared_ptr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
ShutdownCallbackHandle callback_handle;
|
||||
callback_handle.callback = callback_shared_ptr;
|
||||
return callback_handle;
|
||||
}
|
||||
|
||||
bool
|
||||
Context::remove_shutdown_callback(
|
||||
ShutdownType shutdown_type,
|
||||
const ShutdownCallbackHandle & callback_handle)
|
||||
{
|
||||
std::mutex * mutex_ptr = nullptr;
|
||||
std::unordered_set<
|
||||
std::shared_ptr<ShutdownCallbackHandle::ShutdownCallbackType>> * callback_list_ptr;
|
||||
|
||||
switch (shutdown_type) {
|
||||
case ShutdownType::pre_shutdown:
|
||||
mutex_ptr = &pre_shutdown_callbacks_mutex_;
|
||||
callback_list_ptr = &pre_shutdown_callbacks_;
|
||||
break;
|
||||
case ShutdownType::on_shutdown:
|
||||
mutex_ptr = &on_shutdown_callbacks_mutex_;
|
||||
callback_list_ptr = &on_shutdown_callbacks_;
|
||||
break;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(*mutex_ptr);
|
||||
auto callback_shared_ptr = callback_handle.callback.lock();
|
||||
if (callback_shared_ptr == nullptr) {
|
||||
return false;
|
||||
}
|
||||
return callback_list_ptr->erase(callback_shared_ptr) == 1;
|
||||
}
|
||||
|
||||
std::vector<rclcpp::Context::OnShutdownCallback>
|
||||
Context::get_on_shutdown_callbacks() const
|
||||
{
|
||||
return get_shutdown_callback(ShutdownType::on_shutdown);
|
||||
}
|
||||
|
||||
std::vector<rclcpp::Context::PreShutdownCallback>
|
||||
Context::get_pre_shutdown_callbacks() const
|
||||
{
|
||||
return get_shutdown_callback(ShutdownType::pre_shutdown);
|
||||
}
|
||||
|
||||
std::vector<rclcpp::Context::ShutdownCallback>
|
||||
Context::get_shutdown_callback(ShutdownType shutdown_type) const
|
||||
{
|
||||
std::mutex * mutex_ptr = nullptr;
|
||||
const std::unordered_set<
|
||||
std::shared_ptr<ShutdownCallbackHandle::ShutdownCallbackType>> * callback_list_ptr;
|
||||
|
||||
switch (shutdown_type) {
|
||||
case ShutdownType::pre_shutdown:
|
||||
mutex_ptr = &pre_shutdown_callbacks_mutex_;
|
||||
callback_list_ptr = &pre_shutdown_callbacks_;
|
||||
break;
|
||||
case ShutdownType::on_shutdown:
|
||||
mutex_ptr = &on_shutdown_callbacks_mutex_;
|
||||
callback_list_ptr = &on_shutdown_callbacks_;
|
||||
break;
|
||||
}
|
||||
|
||||
std::vector<rclcpp::Context::ShutdownCallback> callbacks;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(*mutex_ptr);
|
||||
for (auto & iter : *callback_list_ptr) {
|
||||
callbacks.emplace_back(*iter);
|
||||
}
|
||||
}
|
||||
|
||||
return callbacks;
|
||||
}
|
||||
|
||||
std::shared_ptr<rcl_context_t>
|
||||
@@ -370,16 +494,15 @@ bool
|
||||
Context::sleep_for(const std::chrono::nanoseconds & nanoseconds)
|
||||
{
|
||||
std::chrono::nanoseconds time_left = nanoseconds;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(interrupt_mutex_);
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
// this will release the lock while waiting
|
||||
interrupt_condition_variable_.wait_for(lock, nanoseconds);
|
||||
time_left -= std::chrono::steady_clock::now() - start;
|
||||
}
|
||||
if (time_left > std::chrono::nanoseconds::zero() && this->is_valid()) {
|
||||
return sleep_for(time_left);
|
||||
}
|
||||
do {
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(interrupt_mutex_);
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
// this will release the lock while waiting
|
||||
interrupt_condition_variable_.wait_for(lock, nanoseconds);
|
||||
time_left -= std::chrono::steady_clock::now() - start;
|
||||
}
|
||||
} while (time_left > std::chrono::nanoseconds::zero() && this->is_valid());
|
||||
// Return true if the timeout elapsed successfully, otherwise false.
|
||||
return this->is_valid();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
// 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/add_guard_condition_to_rcl_wait_set.hpp"
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
|
||||
void
|
||||
add_guard_condition_to_rcl_wait_set(
|
||||
rcl_wait_set_t & wait_set,
|
||||
const rclcpp::GuardCondition & guard_condition)
|
||||
{
|
||||
const auto & gc = guard_condition.get_rcl_guard_condition();
|
||||
|
||||
rcl_ret_t ret = rcl_wait_set_add_guard_condition(&wait_set, &gc, NULL);
|
||||
|
||||
if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(
|
||||
ret, "failed to add guard condition to wait set");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace rclcpp
|
||||
@@ -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
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
@@ -43,20 +45,14 @@ using rclcpp::FutureReturnCode;
|
||||
|
||||
Executor::Executor(const rclcpp::ExecutorOptions & options)
|
||||
: spinning(false),
|
||||
interrupt_guard_condition_(options.context),
|
||||
shutdown_guard_condition_(std::make_shared<rclcpp::GuardCondition>(options.context)),
|
||||
memory_strategy_(options.memory_strategy)
|
||||
{
|
||||
// Store the context for later use.
|
||||
context_ = options.context;
|
||||
|
||||
rcl_guard_condition_options_t guard_condition_options = rcl_guard_condition_get_default_options();
|
||||
rcl_ret_t ret = rcl_guard_condition_init(
|
||||
&interrupt_guard_condition_, context_->get_rcl_context().get(), guard_condition_options);
|
||||
if (RCL_RET_OK != ret) {
|
||||
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) {
|
||||
@@ -66,13 +62,13 @@ Executor::Executor(const rclcpp::ExecutorOptions & options)
|
||||
|
||||
// The number of guard conditions is always at least 2: 1 for the ctrl-c guard cond,
|
||||
// and one for the executor's guard cond (interrupt_guard_condition_)
|
||||
memory_strategy_->add_guard_condition(&shutdown_guard_condition_->get_rcl_guard_condition());
|
||||
memory_strategy_->add_guard_condition(*shutdown_guard_condition_.get());
|
||||
|
||||
// Put the executor's guard condition in
|
||||
memory_strategy_->add_guard_condition(&interrupt_guard_condition_);
|
||||
memory_strategy_->add_guard_condition(interrupt_guard_condition_);
|
||||
rcl_allocator_t allocator = memory_strategy_->get_allocator();
|
||||
|
||||
ret = rcl_wait_set_init(
|
||||
rcl_ret_t ret = rcl_wait_set_init(
|
||||
&wait_set_,
|
||||
0, 2, 0, 0, 0, 0,
|
||||
context_->get_rcl_context().get(),
|
||||
@@ -82,12 +78,6 @@ Executor::Executor(const rclcpp::ExecutorOptions & options)
|
||||
"rclcpp",
|
||||
"failed to create wait set: %s", rcl_get_error_string().str);
|
||||
rcl_reset_error();
|
||||
if (rcl_guard_condition_fini(&interrupt_guard_condition_) != RCL_RET_OK) {
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
"rclcpp",
|
||||
"failed to destroy guard condition: %s", rcl_get_error_string().str);
|
||||
rcl_reset_error();
|
||||
}
|
||||
throw_from_rcl_error(ret, "Failed to create wait set in Executor constructor");
|
||||
}
|
||||
}
|
||||
@@ -117,7 +107,7 @@ Executor::~Executor()
|
||||
weak_groups_to_nodes_associated_with_executor_.clear();
|
||||
weak_groups_to_nodes_.clear();
|
||||
for (const auto & pair : weak_nodes_to_guard_conditions_) {
|
||||
auto & guard_condition = pair.second;
|
||||
auto guard_condition = pair.second;
|
||||
memory_strategy_->remove_guard_condition(guard_condition);
|
||||
}
|
||||
weak_nodes_to_guard_conditions_.clear();
|
||||
@@ -129,15 +119,17 @@ Executor::~Executor()
|
||||
"failed to destroy wait set: %s", rcl_get_error_string().str);
|
||||
rcl_reset_error();
|
||||
}
|
||||
// Finalize the interrupt guard condition.
|
||||
if (rcl_guard_condition_fini(&interrupt_guard_condition_) != RCL_RET_OK) {
|
||||
// Remove and release the sigint guard condition
|
||||
memory_strategy_->remove_guard_condition(shutdown_guard_condition_.get());
|
||||
memory_strategy_->remove_guard_condition(&interrupt_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 destroy guard condition: %s", rcl_get_error_string().str);
|
||||
"failed to remove registered on_shutdown callback");
|
||||
rcl_reset_error();
|
||||
}
|
||||
// Remove and release the sigint guard condition
|
||||
memory_strategy_->remove_guard_condition(&shutdown_guard_condition_->get_rcl_guard_condition());
|
||||
}
|
||||
|
||||
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
||||
@@ -182,14 +174,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,
|
||||
@@ -226,17 +216,20 @@ Executor::add_callback_group_to_map(
|
||||
// Also add to the map that contains all callback groups
|
||||
weak_groups_to_nodes_.insert(std::make_pair(weak_group_ptr, node_ptr));
|
||||
if (is_new_node) {
|
||||
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr node_weak_ptr(node_ptr);
|
||||
weak_nodes_to_guard_conditions_[node_weak_ptr] = node_ptr->get_notify_guard_condition();
|
||||
const auto & gc = node_ptr->get_notify_guard_condition();
|
||||
weak_nodes_to_guard_conditions_[node_ptr] = &gc;
|
||||
if (notify) {
|
||||
// Interrupt waiting to handle new node
|
||||
rcl_ret_t ret = rcl_trigger_guard_condition(&interrupt_guard_condition_);
|
||||
if (ret != RCL_RET_OK) {
|
||||
throw_from_rcl_error(ret, "Failed to trigger guard condition on callback group add");
|
||||
try {
|
||||
interrupt_guard_condition_.trigger();
|
||||
} catch (const rclcpp::exceptions::RCLError & ex) {
|
||||
throw std::runtime_error(
|
||||
std::string(
|
||||
"Failed to trigger guard condition on callback group add: ") + ex.what());
|
||||
}
|
||||
}
|
||||
// Add the node's notify condition to the guard condition handles
|
||||
memory_strategy_->add_guard_condition(node_ptr->get_notify_guard_condition());
|
||||
memory_strategy_->add_guard_condition(gc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,21 +253,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);
|
||||
}
|
||||
|
||||
@@ -303,15 +300,17 @@ Executor::remove_callback_group_from_map(
|
||||
if (!has_node(node_ptr, weak_groups_to_nodes_associated_with_executor_) &&
|
||||
!has_node(node_ptr, weak_groups_associated_with_executor_to_nodes_))
|
||||
{
|
||||
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr node_weak_ptr(node_ptr);
|
||||
weak_nodes_to_guard_conditions_.erase(node_weak_ptr);
|
||||
weak_nodes_to_guard_conditions_.erase(node_ptr);
|
||||
if (notify) {
|
||||
rcl_ret_t ret = rcl_trigger_guard_condition(&interrupt_guard_condition_);
|
||||
if (ret != RCL_RET_OK) {
|
||||
throw_from_rcl_error(ret, "Failed to trigger guard condition on callback group remove");
|
||||
try {
|
||||
interrupt_guard_condition_.trigger();
|
||||
} catch (const rclcpp::exceptions::RCLError & ex) {
|
||||
throw std::runtime_error(
|
||||
std::string(
|
||||
"Failed to trigger guard condition on callback group remove: ") + ex.what());
|
||||
}
|
||||
}
|
||||
memory_strategy_->remove_guard_condition(node_ptr->get_notify_guard_condition());
|
||||
memory_strategy_->remove_guard_condition(&node_ptr->get_notify_guard_condition());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -440,7 +439,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 +473,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);
|
||||
}
|
||||
|
||||
@@ -482,9 +481,11 @@ void
|
||||
Executor::cancel()
|
||||
{
|
||||
spinning.store(false);
|
||||
rcl_ret_t ret = rcl_trigger_guard_condition(&interrupt_guard_condition_);
|
||||
if (ret != RCL_RET_OK) {
|
||||
throw_from_rcl_error(ret, "Failed to trigger guard condition in cancel");
|
||||
try {
|
||||
interrupt_guard_condition_.trigger();
|
||||
} catch (const rclcpp::exceptions::RCLError & ex) {
|
||||
throw std::runtime_error(
|
||||
std::string("Failed to trigger guard condition in cancel: ") + ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -505,9 +506,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) {
|
||||
@@ -523,9 +530,12 @@ Executor::execute_any_executable(AnyExecutable & any_exec)
|
||||
any_exec.callback_group->can_be_taken_from().store(true);
|
||||
// Wake the wait, because it may need to be recalculated or work that
|
||||
// was previously blocked is now available.
|
||||
rcl_ret_t ret = rcl_trigger_guard_condition(&interrupt_guard_condition_);
|
||||
if (ret != RCL_RET_OK) {
|
||||
throw_from_rcl_error(ret, "Failed to trigger guard condition from execute_any_executable");
|
||||
try {
|
||||
interrupt_guard_condition_.trigger();
|
||||
} catch (const rclcpp::exceptions::RCLError & ex) {
|
||||
throw std::runtime_error(
|
||||
std::string(
|
||||
"Failed to trigger guard condition from execute_any_executable: ") + ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -581,8 +591,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 +678,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 +828,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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include "rclcpp/memory_strategy.hpp"
|
||||
#include "rclcpp/executors/static_single_threaded_executor.hpp"
|
||||
#include "rclcpp/detail/add_guard_condition_to_rcl_wait_set.hpp"
|
||||
|
||||
using rclcpp::executors::StaticExecutorEntitiesCollector;
|
||||
|
||||
@@ -61,8 +62,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 +74,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 +105,15 @@ 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()) {
|
||||
const auto & gc = node_ptr->get_notify_guard_condition();
|
||||
weak_nodes_to_guard_conditions_[node_ptr] = &gc;
|
||||
}
|
||||
}
|
||||
new_nodes_.clear();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -261,23 +267,20 @@ StaticExecutorEntitiesCollector::refresh_wait_set(std::chrono::nanoseconds timeo
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
void
|
||||
StaticExecutorEntitiesCollector::add_to_wait_set(rcl_wait_set_t * wait_set)
|
||||
{
|
||||
// Add waitable guard conditions (one for each registered node) into the wait set.
|
||||
for (const auto & pair : weak_nodes_to_guard_conditions_) {
|
||||
auto & gc = pair.second;
|
||||
rcl_ret_t ret = rcl_wait_set_add_guard_condition(wait_set, gc, NULL);
|
||||
if (ret != RCL_RET_OK) {
|
||||
throw std::runtime_error("Executor waitable: couldn't add guard condition to wait set");
|
||||
}
|
||||
detail::add_guard_condition_to_rcl_wait_set(*wait_set, *gc);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
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 +293,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 +332,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;
|
||||
@@ -433,8 +439,9 @@ StaticExecutorEntitiesCollector::is_ready(rcl_wait_set_t * p_wait_set)
|
||||
auto found_guard_condition = std::find_if(
|
||||
weak_nodes_to_guard_conditions_.begin(), weak_nodes_to_guard_conditions_.end(),
|
||||
[&](std::pair<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr,
|
||||
const rcl_guard_condition_t *> pair) -> bool {
|
||||
return pair.second == p_wait_set->guard_conditions[i];
|
||||
const GuardCondition *> pair) -> bool {
|
||||
const rcl_guard_condition_t & rcl_gc = pair.second->get_rcl_guard_condition();
|
||||
return &rcl_gc == p_wait_set->guard_conditions[i];
|
||||
});
|
||||
if (found_guard_condition != weak_nodes_to_guard_conditions_.end()) {
|
||||
return true;
|
||||
@@ -467,13 +474,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(
|
||||
|
||||
@@ -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()) {
|
||||
@@ -138,9 +139,7 @@ StaticSingleThreadedExecutor::add_callback_group(
|
||||
bool is_new_node = entities_collector_->add_callback_group(group_ptr, node_ptr);
|
||||
if (is_new_node && notify) {
|
||||
// Interrupt waiting to handle new node
|
||||
if (rcl_trigger_guard_condition(&interrupt_guard_condition_) != RCL_RET_OK) {
|
||||
throw std::runtime_error(rcl_get_error_string().str);
|
||||
}
|
||||
interrupt_guard_condition_.trigger();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,9 +150,7 @@ StaticSingleThreadedExecutor::add_node(
|
||||
bool is_new_node = entities_collector_->add_node(node_ptr);
|
||||
if (is_new_node && notify) {
|
||||
// Interrupt waiting to handle new node
|
||||
if (rcl_trigger_guard_condition(&interrupt_guard_condition_) != RCL_RET_OK) {
|
||||
throw std::runtime_error(rcl_get_error_string().str);
|
||||
}
|
||||
interrupt_guard_condition_.trigger();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,9 +167,7 @@ StaticSingleThreadedExecutor::remove_callback_group(
|
||||
bool node_removed = entities_collector_->remove_callback_group(group_ptr);
|
||||
// If the node was matched and removed, interrupt waiting
|
||||
if (node_removed && notify) {
|
||||
if (rcl_trigger_guard_condition(&interrupt_guard_condition_) != RCL_RET_OK) {
|
||||
throw std::runtime_error(rcl_get_error_string().str);
|
||||
}
|
||||
interrupt_guard_condition_.trigger();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,9 +181,7 @@ StaticSingleThreadedExecutor::remove_node(
|
||||
}
|
||||
// If the node was matched and removed, interrupt waiting
|
||||
if (notify) {
|
||||
if (rcl_trigger_guard_condition(&interrupt_guard_condition_) != RCL_RET_OK) {
|
||||
throw std::runtime_error(rcl_get_error_string().str);
|
||||
}
|
||||
interrupt_guard_condition_.trigger();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,7 +230,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;
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include "rcl/error_handling.h"
|
||||
#include "rcl/types.h"
|
||||
#include "rclcpp/detail/add_guard_condition_to_rcl_wait_set.hpp"
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/logging.hpp"
|
||||
#include "rclcpp/node.hpp"
|
||||
@@ -40,19 +41,9 @@ GraphListener::GraphListener(const std::shared_ptr<Context> & parent_context)
|
||||
: weak_parent_context_(parent_context),
|
||||
rcl_parent_context_(parent_context->get_rcl_context()),
|
||||
is_started_(false),
|
||||
is_shutdown_(false)
|
||||
is_shutdown_(false),
|
||||
interrupt_guard_condition_(parent_context)
|
||||
{
|
||||
// TODO(wjwwood): make a guard condition class in rclcpp so this can be tracked
|
||||
// automatically with the rcl guard condition
|
||||
// hold on to this context to prevent it from going out of scope while this
|
||||
// guard condition is using it.
|
||||
rcl_ret_t ret = rcl_guard_condition_init(
|
||||
&interrupt_guard_condition_,
|
||||
rcl_parent_context_.get(),
|
||||
rcl_guard_condition_get_default_options());
|
||||
if (RCL_RET_OK != ret) {
|
||||
throw_from_rcl_error(ret, "failed to create interrupt guard condition");
|
||||
}
|
||||
}
|
||||
|
||||
GraphListener::~GraphListener()
|
||||
@@ -159,10 +150,7 @@ GraphListener::run_loop()
|
||||
throw_from_rcl_error(ret, "failed to clear wait set");
|
||||
}
|
||||
// Put the interrupt guard condition in the wait set.
|
||||
ret = rcl_wait_set_add_guard_condition(&wait_set_, &interrupt_guard_condition_, NULL);
|
||||
if (RCL_RET_OK != ret) {
|
||||
throw_from_rcl_error(ret, "failed to add interrupt guard condition to wait set");
|
||||
}
|
||||
detail::add_guard_condition_to_rcl_wait_set(wait_set_, interrupt_guard_condition_);
|
||||
|
||||
// Put graph guard conditions for each node into the wait set.
|
||||
std::vector<size_t> graph_gc_indexes(node_graph_interfaces_size, 0u);
|
||||
@@ -211,19 +199,16 @@ GraphListener::run_loop()
|
||||
}
|
||||
|
||||
static void
|
||||
interrupt_(rcl_guard_condition_t * interrupt_guard_condition)
|
||||
interrupt_(GuardCondition * interrupt_guard_condition)
|
||||
{
|
||||
rcl_ret_t ret = rcl_trigger_guard_condition(interrupt_guard_condition);
|
||||
if (RCL_RET_OK != ret) {
|
||||
throw_from_rcl_error(ret, "failed to trigger the interrupt guard condition");
|
||||
}
|
||||
interrupt_guard_condition->trigger();
|
||||
}
|
||||
|
||||
static void
|
||||
acquire_nodes_lock_(
|
||||
std::mutex * node_graph_interfaces_barrier_mutex,
|
||||
std::mutex * node_graph_interfaces_mutex,
|
||||
rcl_guard_condition_t * interrupt_guard_condition)
|
||||
GuardCondition * interrupt_guard_condition)
|
||||
{
|
||||
{
|
||||
// Acquire this lock to prevent the run loop from re-locking the
|
||||
@@ -351,10 +336,6 @@ GraphListener::__shutdown()
|
||||
interrupt_(&interrupt_guard_condition_);
|
||||
listener_thread_.join();
|
||||
}
|
||||
rcl_ret_t ret = rcl_guard_condition_fini(&interrupt_guard_condition_);
|
||||
if (RCL_RET_OK != ret) {
|
||||
throw_from_rcl_error(ret, "failed to finalize interrupt guard condition");
|
||||
}
|
||||
if (is_started_) {
|
||||
cleanup_wait_set();
|
||||
}
|
||||
|
||||
@@ -20,19 +20,20 @@
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
GuardCondition::GuardCondition(rclcpp::Context::SharedPtr context)
|
||||
GuardCondition::GuardCondition(
|
||||
rclcpp::Context::SharedPtr context,
|
||||
rcl_guard_condition_options_t guard_condition_options)
|
||||
: context_(context), rcl_guard_condition_{rcl_get_zero_initialized_guard_condition()}
|
||||
{
|
||||
if (!context_) {
|
||||
throw std::invalid_argument("context argument unexpectedly nullptr");
|
||||
}
|
||||
rcl_guard_condition_options_t guard_condition_options = rcl_guard_condition_get_default_options();
|
||||
rcl_ret_t ret = rcl_guard_condition_init(
|
||||
&this->rcl_guard_condition_,
|
||||
context_->get_rcl_context().get(),
|
||||
guard_condition_options);
|
||||
if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret);
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "failed to create guard condition");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +46,7 @@ GuardCondition::~GuardCondition()
|
||||
} catch (const std::exception & exception) {
|
||||
RCLCPP_ERROR(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"Error in destruction of rcl guard condition: %s", exception.what());
|
||||
"failed to finalize guard condition: %s", exception.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,6 +57,12 @@ GuardCondition::get_context() const
|
||||
return context_;
|
||||
}
|
||||
|
||||
rcl_guard_condition_t &
|
||||
GuardCondition::get_rcl_guard_condition()
|
||||
{
|
||||
return rcl_guard_condition_;
|
||||
}
|
||||
|
||||
const rcl_guard_condition_t &
|
||||
GuardCondition::get_rcl_guard_condition() const
|
||||
{
|
||||
|
||||
@@ -43,7 +43,7 @@ InitOptions::InitOptions(const rcl_init_options_t & init_options)
|
||||
InitOptions::InitOptions(const InitOptions & other)
|
||||
: InitOptions(*other.get_rcl_init_options())
|
||||
{
|
||||
shutdown_on_sigint = other.shutdown_on_sigint;
|
||||
shutdown_on_signal = other.shutdown_on_signal;
|
||||
initialize_logging_ = other.initialize_logging_;
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ InitOptions::operator=(const InitOptions & other)
|
||||
if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "failed to copy rcl init options");
|
||||
}
|
||||
this->shutdown_on_sigint = other.shutdown_on_sigint;
|
||||
this->shutdown_on_signal = other.shutdown_on_signal;
|
||||
this->initialize_logging_ = other.initialize_logging_;
|
||||
}
|
||||
return *this;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,9 @@ using rclcpp::Node;
|
||||
using rclcpp::NodeOptions;
|
||||
using rclcpp::exceptions::throw_from_rcl_error;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
RCLCPP_LOCAL
|
||||
std::string
|
||||
extend_sub_namespace(const std::string & existing_sub_namespace, const std::string & extension)
|
||||
@@ -54,8 +57,14 @@ extend_sub_namespace(const std::string & existing_sub_namespace, const std::stri
|
||||
// Assumption is that the existing_sub_namespace does not need checking
|
||||
// because it would be checked already when it was set with this function.
|
||||
|
||||
// check if the new sub-namespace extension is absolute
|
||||
if (extension.front() == '/') {
|
||||
if (extension.empty()) {
|
||||
throw rclcpp::exceptions::NameValidationError(
|
||||
"sub_namespace",
|
||||
extension.c_str(),
|
||||
"sub-nodes should not extend nodes by an empty sub-namespace",
|
||||
0);
|
||||
} else if (extension.front() == '/') {
|
||||
// check if the new sub-namespace extension is absolute
|
||||
throw rclcpp::exceptions::NameValidationError(
|
||||
"sub_namespace",
|
||||
extension.c_str(),
|
||||
@@ -70,7 +79,7 @@ extend_sub_namespace(const std::string & existing_sub_namespace, const std::stri
|
||||
new_sub_namespace = existing_sub_namespace + "/" + extension;
|
||||
}
|
||||
|
||||
// remove any trailing `/` so that new extensions do no result in `//`
|
||||
// remove any trailing `/` so that new extensions do not result in `//`
|
||||
if (new_sub_namespace.back() == '/') {
|
||||
new_sub_namespace = new_sub_namespace.substr(0, new_sub_namespace.size() - 1);
|
||||
}
|
||||
@@ -86,7 +95,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 {
|
||||
@@ -94,6 +107,8 @@ create_effective_namespace(const std::string & node_namespace, const std::string
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Node::Node(
|
||||
const std::string & node_name,
|
||||
const NodeOptions & options)
|
||||
@@ -222,6 +237,8 @@ Node::Node(
|
||||
node_services_(other.node_services_),
|
||||
node_clock_(other.node_clock_),
|
||||
node_parameters_(other.node_parameters_),
|
||||
node_time_source_(other.node_time_source_),
|
||||
node_waitables_(other.node_waitables_),
|
||||
node_options_(other.node_options_),
|
||||
sub_namespace_(extend_sub_namespace(other.get_sub_namespace(), sub_namespace)),
|
||||
effective_namespace_(create_effective_namespace(other.get_namespace(), sub_namespace_))
|
||||
@@ -482,10 +499,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
|
||||
|
||||
@@ -44,30 +44,15 @@ NodeBase::NodeBase(
|
||||
node_handle_(nullptr),
|
||||
default_callback_group_(nullptr),
|
||||
associated_with_executor_(false),
|
||||
notify_guard_condition_(context),
|
||||
notify_guard_condition_is_valid_(false)
|
||||
{
|
||||
// Setup the guard condition that is notified when changes occur in the graph.
|
||||
rcl_guard_condition_options_t guard_condition_options = rcl_guard_condition_get_default_options();
|
||||
rcl_ret_t ret = rcl_guard_condition_init(
|
||||
¬ify_guard_condition_, context_->get_rcl_context().get(), guard_condition_options);
|
||||
if (ret != RCL_RET_OK) {
|
||||
throw_from_rcl_error(ret, "failed to create interrupt guard condition");
|
||||
}
|
||||
|
||||
// Setup a safe exit lamda to clean up the guard condition in case of an error here.
|
||||
auto finalize_notify_guard_condition = [this]() {
|
||||
// Finalize the interrupt guard condition.
|
||||
if (rcl_guard_condition_fini(¬ify_guard_condition_) != RCL_RET_OK) {
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
"rclcpp",
|
||||
"failed to destroy guard condition: %s", rcl_get_error_string().str);
|
||||
}
|
||||
};
|
||||
|
||||
// Create the rcl node and store it in a shared_ptr with a custom destructor.
|
||||
std::unique_ptr<rcl_node_t> rcl_node(new rcl_node_t(rcl_get_zero_initialized_node()));
|
||||
|
||||
std::shared_ptr<std::recursive_mutex> logging_mutex = get_global_logging_mutex();
|
||||
|
||||
rcl_ret_t ret;
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(*logging_mutex);
|
||||
// TODO(ivanpauno): /rosout Qos should be reconfigurable.
|
||||
@@ -80,9 +65,6 @@ NodeBase::NodeBase(
|
||||
context_->get_rcl_context().get(), &rcl_node_options);
|
||||
}
|
||||
if (ret != RCL_RET_OK) {
|
||||
// Finalize the interrupt guard condition.
|
||||
finalize_notify_guard_condition();
|
||||
|
||||
if (ret == RCL_RET_NODE_INVALID_NAME) {
|
||||
rcl_reset_error(); // discard rcl_node_init error
|
||||
int validation_result;
|
||||
@@ -160,11 +142,6 @@ NodeBase::~NodeBase()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> notify_condition_lock(notify_guard_condition_mutex_);
|
||||
notify_guard_condition_is_valid_ = false;
|
||||
if (rcl_guard_condition_fini(¬ify_guard_condition_) != RCL_RET_OK) {
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
"rclcpp",
|
||||
"failed to destroy guard condition: %s", rcl_get_error_string().str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,13 +184,13 @@ NodeBase::get_rcl_node_handle() const
|
||||
std::shared_ptr<rcl_node_t>
|
||||
NodeBase::get_shared_rcl_node_handle()
|
||||
{
|
||||
return node_handle_;
|
||||
return std::shared_ptr<rcl_node_t>(shared_from_this(), node_handle_.get());
|
||||
}
|
||||
|
||||
std::shared_ptr<const rcl_node_t>
|
||||
NodeBase::get_shared_rcl_node_handle() const
|
||||
{
|
||||
return node_handle_;
|
||||
return std::shared_ptr<const rcl_node_t>(shared_from_this(), node_handle_.get());
|
||||
}
|
||||
|
||||
rclcpp::CallbackGroup::SharedPtr
|
||||
@@ -221,12 +198,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 +215,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 &
|
||||
@@ -262,20 +242,14 @@ NodeBase::get_associated_with_executor_atomic()
|
||||
return associated_with_executor_;
|
||||
}
|
||||
|
||||
rcl_guard_condition_t *
|
||||
rclcpp::GuardCondition &
|
||||
NodeBase::get_notify_guard_condition()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> notify_condition_lock(notify_guard_condition_mutex_);
|
||||
if (!notify_guard_condition_is_valid_) {
|
||||
return nullptr;
|
||||
throw std::runtime_error("failed to get notify guard condition because it is invalid");
|
||||
}
|
||||
return ¬ify_guard_condition_;
|
||||
}
|
||||
|
||||
std::unique_lock<std::recursive_mutex>
|
||||
NodeBase::acquire_notify_guard_condition_lock() const
|
||||
{
|
||||
return std::unique_lock<std::recursive_mutex>(notify_guard_condition_mutex_);
|
||||
return notify_guard_condition_;
|
||||
}
|
||||
|
||||
bool
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -27,6 +28,7 @@
|
||||
#include "rclcpp/expand_topic_or_service_name.hpp"
|
||||
#include "rclcpp/graph_listener.hpp"
|
||||
#include "rclcpp/node_interfaces/node_graph_interface.hpp"
|
||||
#include "rcpputils/scope_exit.hpp"
|
||||
|
||||
using rclcpp::node_interfaces::NodeGraph;
|
||||
using rclcpp::exceptions::throw_from_rcl_error;
|
||||
@@ -178,6 +180,115 @@ NodeGraph::get_service_names_and_types_by_node(
|
||||
return services_and_types;
|
||||
}
|
||||
|
||||
std::map<std::string, std::vector<std::string>>
|
||||
NodeGraph::get_client_names_and_types_by_node(
|
||||
const std::string & node_name,
|
||||
const std::string & namespace_) const
|
||||
{
|
||||
rcl_names_and_types_t service_names_and_types = rcl_get_zero_initialized_names_and_types();
|
||||
auto service_names_and_types_finalizer = rcpputils::make_scope_exit(
|
||||
[&service_names_and_types]() {
|
||||
if (rcl_names_and_types_fini(&service_names_and_types) != RCL_RET_OK) {
|
||||
RCLCPP_ERROR(
|
||||
rclcpp::get_logger("rclcpp"), "could not destroy service names and types");
|
||||
}
|
||||
});
|
||||
rcl_allocator_t allocator = rcl_get_default_allocator();
|
||||
rcl_ret_t ret = rcl_get_client_names_and_types_by_node(
|
||||
node_base_->get_rcl_node_handle(),
|
||||
&allocator,
|
||||
node_name.c_str(),
|
||||
namespace_.c_str(),
|
||||
&service_names_and_types);
|
||||
if (ret != RCL_RET_OK) {
|
||||
throw_from_rcl_error(ret, "failed to get service names and types by node");
|
||||
}
|
||||
|
||||
std::map<std::string, std::vector<std::string>> services_and_types;
|
||||
for (size_t i = 0; i < service_names_and_types.names.size; ++i) {
|
||||
std::string service_name = service_names_and_types.names.data[i];
|
||||
for (size_t j = 0; j < service_names_and_types.types[i].size; ++j) {
|
||||
services_and_types[service_name].emplace_back(service_names_and_types.types[i].data[j]);
|
||||
}
|
||||
}
|
||||
|
||||
return services_and_types;
|
||||
}
|
||||
|
||||
std::map<std::string, std::vector<std::string>>
|
||||
NodeGraph::get_publisher_names_and_types_by_node(
|
||||
const std::string & node_name,
|
||||
const std::string & namespace_,
|
||||
bool no_demangle) const
|
||||
{
|
||||
rcl_names_and_types_t topic_names_and_types = rcl_get_zero_initialized_names_and_types();
|
||||
auto topic_names_and_types_finalizer = rcpputils::make_scope_exit(
|
||||
[&topic_names_and_types]() {
|
||||
if (rcl_names_and_types_fini(&topic_names_and_types) != RCL_RET_OK) {
|
||||
RCLCPP_ERROR(
|
||||
rclcpp::get_logger("rclcpp"), "could not destroy topic names and types");
|
||||
}
|
||||
});
|
||||
rcl_allocator_t allocator = rcl_get_default_allocator();
|
||||
rcl_ret_t ret = rcl_get_publisher_names_and_types_by_node(
|
||||
node_base_->get_rcl_node_handle(),
|
||||
&allocator,
|
||||
no_demangle,
|
||||
node_name.c_str(),
|
||||
namespace_.c_str(),
|
||||
&topic_names_and_types);
|
||||
if (ret != RCL_RET_OK) {
|
||||
throw_from_rcl_error(ret, "failed to get topic names and types by node");
|
||||
}
|
||||
|
||||
std::map<std::string, std::vector<std::string>> topics_and_types;
|
||||
for (size_t i = 0; i < topic_names_and_types.names.size; ++i) {
|
||||
std::string topic_name = topic_names_and_types.names.data[i];
|
||||
for (size_t j = 0; j < topic_names_and_types.types[i].size; ++j) {
|
||||
topics_and_types[topic_name].emplace_back(topic_names_and_types.types[i].data[j]);
|
||||
}
|
||||
}
|
||||
|
||||
return topics_and_types;
|
||||
}
|
||||
|
||||
std::map<std::string, std::vector<std::string>>
|
||||
NodeGraph::get_subscriber_names_and_types_by_node(
|
||||
const std::string & node_name,
|
||||
const std::string & namespace_,
|
||||
bool no_demangle) const
|
||||
{
|
||||
rcl_names_and_types_t topic_names_and_types = rcl_get_zero_initialized_names_and_types();
|
||||
auto topic_names_and_types_finalizer = rcpputils::make_scope_exit(
|
||||
[&topic_names_and_types]() {
|
||||
if (rcl_names_and_types_fini(&topic_names_and_types) != RCL_RET_OK) {
|
||||
RCLCPP_ERROR(
|
||||
rclcpp::get_logger("rclcpp"), "could not destroy topic names and types");
|
||||
}
|
||||
});
|
||||
rcl_allocator_t allocator = rcl_get_default_allocator();
|
||||
rcl_ret_t ret = rcl_get_subscriber_names_and_types_by_node(
|
||||
node_base_->get_rcl_node_handle(),
|
||||
&allocator,
|
||||
no_demangle,
|
||||
node_name.c_str(),
|
||||
namespace_.c_str(),
|
||||
&topic_names_and_types);
|
||||
if (ret != RCL_RET_OK) {
|
||||
throw_from_rcl_error(ret, "failed to get topic names and types by node");
|
||||
}
|
||||
|
||||
std::map<std::string, std::vector<std::string>> topics_and_types;
|
||||
for (size_t i = 0; i < topic_names_and_types.names.size; ++i) {
|
||||
std::string topic_name = topic_names_and_types.names.data[i];
|
||||
for (size_t j = 0; j < topic_names_and_types.types[i].size; ++j) {
|
||||
topics_and_types[topic_name].emplace_back(topic_names_and_types.types[i].data[j]);
|
||||
}
|
||||
}
|
||||
|
||||
return topics_and_types;
|
||||
}
|
||||
|
||||
std::vector<std::string>
|
||||
NodeGraph::get_node_names() const
|
||||
{
|
||||
@@ -206,6 +317,81 @@ NodeGraph::get_node_names() const
|
||||
return nodes;
|
||||
}
|
||||
|
||||
std::vector<std::tuple<std::string, std::string, std::string>>
|
||||
NodeGraph::get_node_names_with_enclaves() const
|
||||
{
|
||||
rcutils_string_array_t node_names_c =
|
||||
rcutils_get_zero_initialized_string_array();
|
||||
rcutils_string_array_t node_namespaces_c =
|
||||
rcutils_get_zero_initialized_string_array();
|
||||
rcutils_string_array_t node_enclaves_c =
|
||||
rcutils_get_zero_initialized_string_array();
|
||||
|
||||
auto allocator = rcl_get_default_allocator();
|
||||
auto ret = rcl_get_node_names_with_enclaves(
|
||||
node_base_->get_rcl_node_handle(),
|
||||
allocator,
|
||||
&node_names_c,
|
||||
&node_namespaces_c,
|
||||
&node_enclaves_c);
|
||||
if (ret != RCL_RET_OK) {
|
||||
auto error_msg =
|
||||
std::string("failed to get node names with enclaves: ") + rcl_get_error_string().str;
|
||||
rcl_reset_error();
|
||||
if (rcutils_string_array_fini(&node_names_c) != RCUTILS_RET_OK) {
|
||||
error_msg += std::string(", failed also to cleanup node names, leaking memory: ") +
|
||||
rcl_get_error_string().str;
|
||||
rcl_reset_error();
|
||||
}
|
||||
if (rcutils_string_array_fini(&node_namespaces_c) != RCUTILS_RET_OK) {
|
||||
error_msg += std::string(", failed also to cleanup node namespaces, leaking memory: ") +
|
||||
rcl_get_error_string().str;
|
||||
rcl_reset_error();
|
||||
}
|
||||
if (rcutils_string_array_fini(&node_enclaves_c) != RCUTILS_RET_OK) {
|
||||
error_msg += std::string(", failed also to cleanup node enclaves, leaking memory: ") +
|
||||
rcl_get_error_string().str;
|
||||
rcl_reset_error();
|
||||
}
|
||||
throw std::runtime_error(error_msg);
|
||||
}
|
||||
|
||||
std::vector<std::tuple<std::string, std::string, std::string>> node_tuples;
|
||||
for (size_t i = 0; i < node_names_c.size; ++i) {
|
||||
if (node_names_c.data[i] && node_namespaces_c.data[i] && node_enclaves_c.data[i]) {
|
||||
node_tuples.emplace_back(
|
||||
std::make_tuple(node_names_c.data[i], node_namespaces_c.data[i], node_enclaves_c.data[i]));
|
||||
}
|
||||
}
|
||||
|
||||
std::string error("failed to finalize array");
|
||||
rcl_ret_t ret_names = rcutils_string_array_fini(&node_names_c);
|
||||
if (ret_names != RCUTILS_RET_OK) {
|
||||
error += std::string(", could not destroy node names, leaking memory: ") +
|
||||
rcl_get_error_string().str;
|
||||
rcl_reset_error();
|
||||
}
|
||||
rcl_ret_t ret_ns = rcutils_string_array_fini(&node_namespaces_c);
|
||||
if (ret_ns != RCUTILS_RET_OK) {
|
||||
error += std::string(", could not destroy node namespaces, leaking memory: ") +
|
||||
rcl_get_error_string().str;
|
||||
rcl_reset_error();
|
||||
}
|
||||
|
||||
rcl_ret_t ret_ecv = rcutils_string_array_fini(&node_enclaves_c);
|
||||
if (ret_ecv != RCUTILS_RET_OK) {
|
||||
error += std::string(", could not destroy node enclaves, leaking memory: ") +
|
||||
rcl_get_error_string().str;
|
||||
rcl_reset_error();
|
||||
}
|
||||
|
||||
if (ret_names != RCUTILS_RET_OK || ret_ns != RCUTILS_RET_OK || ret_ecv != RCUTILS_RET_OK) {
|
||||
throw std::runtime_error(error);
|
||||
}
|
||||
|
||||
return node_tuples;
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, std::string>>
|
||||
NodeGraph::get_node_names_and_namespaces() const
|
||||
{
|
||||
@@ -237,7 +423,6 @@ NodeGraph::get_node_names_and_namespaces() const
|
||||
throw std::runtime_error(error_msg);
|
||||
}
|
||||
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> node_names;
|
||||
node_names.reserve(node_names_c.size);
|
||||
for (size_t i = 0; i < node_names_c.size; ++i) {
|
||||
@@ -348,12 +533,12 @@ NodeGraph::notify_graph_change()
|
||||
}
|
||||
}
|
||||
graph_cv_.notify_all();
|
||||
{
|
||||
auto notify_condition_lock = node_base_->acquire_notify_guard_condition_lock();
|
||||
rcl_ret_t ret = rcl_trigger_guard_condition(node_base_->get_notify_guard_condition());
|
||||
if (RCL_RET_OK != ret) {
|
||||
throw_from_rcl_error(ret, "failed to trigger notify guard condition");
|
||||
}
|
||||
auto & node_gc = node_base_->get_notify_guard_condition();
|
||||
try {
|
||||
node_gc.trigger();
|
||||
} catch (const rclcpp::exceptions::RCLError & ex) {
|
||||
throw std::runtime_error(
|
||||
std::string("failed to notify wait set on graph change: ") + ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,14 +41,12 @@ NodeServices::add_service(
|
||||
}
|
||||
|
||||
// Notify the executor that a new service was created using the parent Node.
|
||||
{
|
||||
auto notify_guard_condition_lock = node_base_->acquire_notify_guard_condition_lock();
|
||||
if (rcl_trigger_guard_condition(node_base_->get_notify_guard_condition()) != RCL_RET_OK) {
|
||||
throw std::runtime_error(
|
||||
std::string("Failed to notify wait set on service creation: ") +
|
||||
rmw_get_error_string().str
|
||||
);
|
||||
}
|
||||
auto & node_gc = node_base_->get_notify_guard_condition();
|
||||
try {
|
||||
node_gc.trigger();
|
||||
} catch (const rclcpp::exceptions::RCLError & ex) {
|
||||
throw std::runtime_error(
|
||||
std::string("failed to notify wait set on service creation: ") + ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,14 +66,12 @@ NodeServices::add_client(
|
||||
}
|
||||
|
||||
// Notify the executor that a new client was created using the parent Node.
|
||||
{
|
||||
auto notify_guard_condition_lock = node_base_->acquire_notify_guard_condition_lock();
|
||||
if (rcl_trigger_guard_condition(node_base_->get_notify_guard_condition()) != RCL_RET_OK) {
|
||||
throw std::runtime_error(
|
||||
std::string("Failed to notify wait set on client creation: ") +
|
||||
rmw_get_error_string().str
|
||||
);
|
||||
}
|
||||
auto & node_gc = node_base_->get_notify_guard_condition();
|
||||
try {
|
||||
node_gc.trigger();
|
||||
} catch (const rclcpp::exceptions::RCLError & ex) {
|
||||
throw std::runtime_error(
|
||||
std::string("failed to notify wait set on client creation: ") + ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,11 +41,15 @@ NodeTimers::add_timer(
|
||||
} else {
|
||||
node_base_->get_default_callback_group()->add_timer(timer);
|
||||
}
|
||||
if (rcl_trigger_guard_condition(node_base_->get_notify_guard_condition()) != RCL_RET_OK) {
|
||||
|
||||
auto & node_gc = node_base_->get_notify_guard_condition();
|
||||
try {
|
||||
node_gc.trigger();
|
||||
} catch (const rclcpp::exceptions::RCLError & ex) {
|
||||
throw std::runtime_error(
|
||||
std::string("Failed to notify wait set on timer creation: ") +
|
||||
rmw_get_error_string().str);
|
||||
std::string("failed to notify wait set on timer creation: ") + ex.what());
|
||||
}
|
||||
|
||||
TRACEPOINT(
|
||||
rclcpp_timer_link_node,
|
||||
static_cast<const void *>(timer->get_timer_handle().get()),
|
||||
|
||||
@@ -60,13 +60,12 @@ NodeTopics::add_publisher(
|
||||
}
|
||||
|
||||
// Notify the executor that a new publisher was created using the parent Node.
|
||||
{
|
||||
auto notify_guard_condition_lock = node_base_->acquire_notify_guard_condition_lock();
|
||||
if (rcl_trigger_guard_condition(node_base_->get_notify_guard_condition()) != RCL_RET_OK) {
|
||||
throw std::runtime_error(
|
||||
std::string("Failed to notify wait set on publisher creation: ") +
|
||||
rmw_get_error_string().str);
|
||||
}
|
||||
auto & node_gc = node_base_->get_notify_guard_condition();
|
||||
try {
|
||||
node_gc.trigger();
|
||||
} catch (const rclcpp::exceptions::RCLError & ex) {
|
||||
throw std::runtime_error(
|
||||
std::string("failed to notify wait set on publisher creation: ") + ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,13 +107,12 @@ NodeTopics::add_subscription(
|
||||
}
|
||||
|
||||
// Notify the executor that a new subscription was created using the parent Node.
|
||||
{
|
||||
auto notify_guard_condition_lock = node_base_->acquire_notify_guard_condition_lock();
|
||||
auto ret = rcl_trigger_guard_condition(node_base_->get_notify_guard_condition());
|
||||
if (ret != RCL_RET_OK) {
|
||||
using rclcpp::exceptions::throw_from_rcl_error;
|
||||
throw_from_rcl_error(ret, "failed to notify wait set on subscription creation");
|
||||
}
|
||||
auto & node_gc = node_base_->get_notify_guard_condition();
|
||||
try {
|
||||
node_gc.trigger();
|
||||
} catch (const rclcpp::exceptions::RCLError & ex) {
|
||||
throw std::runtime_error(
|
||||
std::string("failed to notify wait set on subscription creation: ") + ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,14 +41,12 @@ NodeWaitables::add_waitable(
|
||||
}
|
||||
|
||||
// Notify the executor that a new waitable was created using the parent Node.
|
||||
{
|
||||
auto notify_guard_condition_lock = node_base_->acquire_notify_guard_condition_lock();
|
||||
if (rcl_trigger_guard_condition(node_base_->get_notify_guard_condition()) != RCL_RET_OK) {
|
||||
throw std::runtime_error(
|
||||
std::string("Failed to notify wait set on waitable creation: ") +
|
||||
rmw_get_error_string().str
|
||||
);
|
||||
}
|
||||
auto & node_gc = node_base_->get_notify_guard_condition();
|
||||
try {
|
||||
node_gc.trigger();
|
||||
} catch (const rclcpp::exceptions::RCLError & ex) {
|
||||
throw std::runtime_error(
|
||||
std::string("failed to notify wait set on waitable creation: ") + ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,10 +29,10 @@ ParameterEventCallbackHandle::SharedPtr
|
||||
ParameterEventHandler::add_parameter_event_callback(
|
||||
ParameterEventCallbackType callback)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
std::lock_guard<std::recursive_mutex> lock(callbacks_->mutex_);
|
||||
auto handle = std::make_shared<ParameterEventCallbackHandle>();
|
||||
handle->callback = callback;
|
||||
event_callbacks_.emplace_front(handle);
|
||||
callbacks_->event_callbacks_.emplace_front(handle);
|
||||
|
||||
return handle;
|
||||
}
|
||||
@@ -41,15 +41,15 @@ void
|
||||
ParameterEventHandler::remove_parameter_event_callback(
|
||||
ParameterEventCallbackHandle::SharedPtr callback_handle)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
std::lock_guard<std::recursive_mutex> lock(callbacks_->mutex_);
|
||||
auto it = std::find_if(
|
||||
event_callbacks_.begin(),
|
||||
event_callbacks_.end(),
|
||||
callbacks_->event_callbacks_.begin(),
|
||||
callbacks_->event_callbacks_.end(),
|
||||
[callback_handle](const auto & weak_handle) {
|
||||
return callback_handle.get() == weak_handle.lock().get();
|
||||
});
|
||||
if (it != event_callbacks_.end()) {
|
||||
event_callbacks_.erase(it);
|
||||
if (it != callbacks_->event_callbacks_.end()) {
|
||||
callbacks_->event_callbacks_.erase(it);
|
||||
} else {
|
||||
throw std::runtime_error("Callback doesn't exist");
|
||||
}
|
||||
@@ -61,7 +61,7 @@ ParameterEventHandler::add_parameter_callback(
|
||||
ParameterCallbackType callback,
|
||||
const std::string & node_name)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
std::lock_guard<std::recursive_mutex> lock(callbacks_->mutex_);
|
||||
auto full_node_name = resolve_path(node_name);
|
||||
|
||||
auto handle = std::make_shared<ParameterCallbackHandle>();
|
||||
@@ -69,7 +69,7 @@ ParameterEventHandler::add_parameter_callback(
|
||||
handle->parameter_name = parameter_name;
|
||||
handle->node_name = full_node_name;
|
||||
// the last callback registered is executed first.
|
||||
parameter_callbacks_[{parameter_name, full_node_name}].emplace_front(handle);
|
||||
callbacks_->parameter_callbacks_[{parameter_name, full_node_name}].emplace_front(handle);
|
||||
|
||||
return handle;
|
||||
}
|
||||
@@ -78,9 +78,9 @@ void
|
||||
ParameterEventHandler::remove_parameter_callback(
|
||||
ParameterCallbackHandle::SharedPtr callback_handle)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
std::lock_guard<std::recursive_mutex> lock(callbacks_->mutex_);
|
||||
auto handle = callback_handle.get();
|
||||
auto & container = parameter_callbacks_[{handle->parameter_name, handle->node_name}];
|
||||
auto & container = callbacks_->parameter_callbacks_[{handle->parameter_name, handle->node_name}];
|
||||
auto it = std::find_if(
|
||||
container.begin(),
|
||||
container.end(),
|
||||
@@ -90,7 +90,7 @@ ParameterEventHandler::remove_parameter_callback(
|
||||
if (it != container.end()) {
|
||||
container.erase(it);
|
||||
if (container.empty()) {
|
||||
parameter_callbacks_.erase({handle->parameter_name, handle->node_name});
|
||||
callbacks_->parameter_callbacks_.erase({handle->parameter_name, handle->node_name});
|
||||
}
|
||||
} else {
|
||||
throw std::runtime_error("Callback doesn't exist");
|
||||
@@ -101,8 +101,8 @@ bool
|
||||
ParameterEventHandler::get_parameter_from_event(
|
||||
const rcl_interfaces::msg::ParameterEvent & event,
|
||||
rclcpp::Parameter & parameter,
|
||||
const std::string parameter_name,
|
||||
const std::string node_name)
|
||||
const std::string & parameter_name,
|
||||
const std::string & node_name)
|
||||
{
|
||||
if (event.node != node_name) {
|
||||
return false;
|
||||
@@ -128,8 +128,8 @@ ParameterEventHandler::get_parameter_from_event(
|
||||
rclcpp::Parameter
|
||||
ParameterEventHandler::get_parameter_from_event(
|
||||
const rcl_interfaces::msg::ParameterEvent & event,
|
||||
const std::string parameter_name,
|
||||
const std::string node_name)
|
||||
const std::string & parameter_name,
|
||||
const std::string & node_name)
|
||||
{
|
||||
rclcpp::Parameter p;
|
||||
if (!get_parameter_from_event(event, p, parameter_name, node_name)) {
|
||||
@@ -158,7 +158,7 @@ ParameterEventHandler::get_parameters_from_event(
|
||||
}
|
||||
|
||||
void
|
||||
ParameterEventHandler::event_callback(const rcl_interfaces::msg::ParameterEvent & event)
|
||||
ParameterEventHandler::Callbacks::event_callback(const rcl_interfaces::msg::ParameterEvent & event)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
|
||||
|
||||
@@ -51,14 +51,13 @@ QOSEventHandlerBase::get_number_of_ready_events()
|
||||
}
|
||||
|
||||
/// Add the Waitable to a wait set.
|
||||
bool
|
||||
void
|
||||
QOSEventHandlerBase::add_to_wait_set(rcl_wait_set_t * wait_set)
|
||||
{
|
||||
rcl_ret_t ret = rcl_wait_set_add_event(wait_set, &event_handle_, &wait_set_event_index_);
|
||||
if (RCL_RET_OK != ret) {
|
||||
exceptions::throw_from_rcl_error(ret, "Couldn't add event to wait set");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Check if the Waitable is ready.
|
||||
|
||||
@@ -30,80 +30,131 @@
|
||||
#endif
|
||||
|
||||
#include "rclcpp/logging.hpp"
|
||||
#include "rclcpp/utilities.hpp"
|
||||
#include "rcutils/strerror.h"
|
||||
#include "rmw/impl/cpp/demangle.hpp"
|
||||
|
||||
using rclcpp::SignalHandler;
|
||||
using rclcpp::SignalHandlerOptions;
|
||||
|
||||
// initialize static storage in SignalHandler class
|
||||
SignalHandler::signal_handler_type SignalHandler::old_signal_handler_;
|
||||
std::atomic_bool SignalHandler::signal_received_ = ATOMIC_VAR_INIT(false);
|
||||
std::atomic_bool SignalHandler::wait_for_signal_is_setup_ = ATOMIC_VAR_INIT(false);
|
||||
#if defined(_WIN32)
|
||||
HANDLE SignalHandler::signal_handler_sem_;
|
||||
#elif defined(__APPLE__)
|
||||
dispatch_semaphore_t SignalHandler::signal_handler_sem_;
|
||||
#else // posix
|
||||
sem_t SignalHandler::signal_handler_sem_;
|
||||
SignalHandler::signal_handler_type
|
||||
SignalHandler::set_signal_handler(
|
||||
int signal_value,
|
||||
const SignalHandler::signal_handler_type & signal_handler)
|
||||
{
|
||||
bool signal_handler_install_failed;
|
||||
SignalHandler::signal_handler_type old_signal_handler;
|
||||
#if defined(RCLCPP_HAS_SIGACTION)
|
||||
ssize_t ret = sigaction(signal_value, &signal_handler, &old_signal_handler);
|
||||
signal_handler_install_failed = (ret == -1);
|
||||
#else
|
||||
old_signal_handler = std::signal(signal_value, signal_handler);
|
||||
signal_handler_install_failed = (old_signal_handler == SIG_ERR);
|
||||
#endif
|
||||
if (signal_handler_install_failed) {
|
||||
char error_string[1024];
|
||||
rcutils_strerror(error_string, sizeof(error_string));
|
||||
auto msg =
|
||||
"Failed to set signal handler (" + std::to_string(errno) + "): " + error_string;
|
||||
throw std::runtime_error(msg);
|
||||
}
|
||||
return old_signal_handler;
|
||||
}
|
||||
|
||||
// The logger must be initialized before the local static variable signal_handler,
|
||||
// from the method get_global_signal_handler(), so that it is destructed after
|
||||
// it, because the destructor of SignalHandler uses this logger object.
|
||||
static rclcpp::Logger g_logger = rclcpp::get_logger("rclcpp");
|
||||
// Unfortunately macros (or duplicated code) are needed here,
|
||||
// as the signal handler must be a function pointer.
|
||||
#if defined(RCLCPP_HAS_SIGACTION)
|
||||
void
|
||||
SignalHandler::signal_handler(
|
||||
int signum, siginfo_t * siginfo, void * context)
|
||||
{
|
||||
RCLCPP_INFO(SignalHandler::get_logger(), "signal_handler(signum=%d)", signum);
|
||||
auto & instance = SignalHandler::get_global_signal_handler();
|
||||
|
||||
auto old_signal_handler = instance.get_old_signal_handler(signum);
|
||||
if (old_signal_handler.sa_flags & SA_SIGINFO) {
|
||||
if (old_signal_handler.sa_sigaction != NULL) {
|
||||
old_signal_handler.sa_sigaction(signum, siginfo, context);
|
||||
}
|
||||
} else {
|
||||
if (
|
||||
old_signal_handler.sa_handler != NULL && /* Is set */
|
||||
old_signal_handler.sa_handler != SIG_DFL && /* Is not default*/
|
||||
old_signal_handler.sa_handler != SIG_IGN) /* Is not ignored */
|
||||
{
|
||||
old_signal_handler.sa_handler(signum);
|
||||
}
|
||||
}
|
||||
instance.signal_handler_common();
|
||||
}
|
||||
#else
|
||||
void
|
||||
SignalHandler::signal_handler(int signum)
|
||||
{
|
||||
RCLCPP_INFO(SignalHandler::get_logger(), "signal_handler(signum=%d)", signum);
|
||||
auto & instance = SignalHandler::get_global_signal_handler();
|
||||
auto old_signal_handler = instance.get_old_signal_handler(signum);
|
||||
if (
|
||||
SIG_ERR != old_signal_handler && SIG_IGN != old_signal_handler &&
|
||||
SIG_DFL != old_signal_handler)
|
||||
{
|
||||
old_signal_handler(signum);
|
||||
}
|
||||
instance.signal_handler_common();
|
||||
}
|
||||
#endif
|
||||
|
||||
rclcpp::Logger &
|
||||
SignalHandler::get_logger()
|
||||
{
|
||||
return g_logger;
|
||||
return SignalHandler::get_global_signal_handler().logger_;
|
||||
}
|
||||
|
||||
SignalHandler &
|
||||
SignalHandler::get_global_signal_handler()
|
||||
{
|
||||
// This is initialized after the g_logger static global, ensuring
|
||||
// SignalHandler::get_logger() may be called from the destructor of
|
||||
// SignalHandler, according to this:
|
||||
//
|
||||
// Variables declared at block scope with the specifier static have static
|
||||
// storage duration but are initialized the first time control passes
|
||||
// through their declaration (unless their initialization is zero- or
|
||||
// constant-initialization, which can be performed before the block is
|
||||
// first entered). On all further calls, the declaration is skipped.
|
||||
//
|
||||
// -- https://en.cppreference.com/w/cpp/language/storage_duration#Static_local_variables
|
||||
//
|
||||
// Which is guaranteed to occur after static initialization for global (see:
|
||||
// https://en.cppreference.com/w/cpp/language/initialization#Static_initialization),
|
||||
// which is when g_logger will be initialized.
|
||||
// And destruction will occur in the reverse order.
|
||||
static SignalHandler signal_handler;
|
||||
return signal_handler;
|
||||
}
|
||||
|
||||
bool
|
||||
SignalHandler::install()
|
||||
SignalHandler::install(SignalHandlerOptions signal_handler_options)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(install_mutex_);
|
||||
bool already_installed = installed_.exchange(true);
|
||||
if (already_installed) {
|
||||
return false;
|
||||
}
|
||||
if (signal_handler_options == SignalHandlerOptions::None) {
|
||||
return true;
|
||||
}
|
||||
signal_handlers_options_ = signal_handler_options;
|
||||
try {
|
||||
setup_wait_for_signal();
|
||||
signal_received_.store(false);
|
||||
|
||||
SignalHandler::signal_handler_type signal_handler_argument;
|
||||
SignalHandler::signal_handler_type handler_argument;
|
||||
#if defined(RCLCPP_HAS_SIGACTION)
|
||||
memset(&signal_handler_argument, 0, sizeof(signal_handler_argument));
|
||||
sigemptyset(&signal_handler_argument.sa_mask);
|
||||
signal_handler_argument.sa_sigaction = signal_handler;
|
||||
signal_handler_argument.sa_flags = SA_SIGINFO;
|
||||
memset(&handler_argument, 0, sizeof(handler_argument));
|
||||
sigemptyset(&handler_argument.sa_mask);
|
||||
handler_argument.sa_sigaction = &this->signal_handler;
|
||||
handler_argument.sa_flags = SA_SIGINFO;
|
||||
#else
|
||||
signal_handler_argument = signal_handler;
|
||||
handler_argument = &this->signal_handler;
|
||||
#endif
|
||||
if (
|
||||
signal_handler_options == SignalHandlerOptions::SigInt ||
|
||||
signal_handler_options == SignalHandlerOptions::All)
|
||||
{
|
||||
old_sigint_handler_ = set_signal_handler(SIGINT, handler_argument);
|
||||
}
|
||||
|
||||
old_signal_handler_ = SignalHandler::set_signal_handler(SIGINT, signal_handler_argument);
|
||||
if (
|
||||
signal_handler_options == SignalHandlerOptions::SigTerm ||
|
||||
signal_handler_options == SignalHandlerOptions::All)
|
||||
{
|
||||
old_sigterm_handler_ = set_signal_handler(SIGTERM, handler_argument);
|
||||
}
|
||||
|
||||
signal_handler_thread_ = std::thread(&SignalHandler::deferred_signal_handler, this);
|
||||
} catch (...) {
|
||||
@@ -125,7 +176,19 @@ SignalHandler::uninstall()
|
||||
try {
|
||||
// TODO(wjwwood): what happens if someone overrides our signal handler then calls uninstall?
|
||||
// I think we need to assert that we're the current signal handler, and mitigate if not.
|
||||
set_signal_handler(SIGINT, old_signal_handler_);
|
||||
if (
|
||||
SignalHandlerOptions::SigInt == signal_handlers_options_ ||
|
||||
SignalHandlerOptions::All == signal_handlers_options_)
|
||||
{
|
||||
set_signal_handler(SIGINT, old_sigint_handler_);
|
||||
}
|
||||
if (
|
||||
SignalHandlerOptions::SigTerm == signal_handlers_options_ ||
|
||||
SignalHandlerOptions::All == signal_handlers_options_)
|
||||
{
|
||||
set_signal_handler(SIGTERM, old_sigterm_handler_);
|
||||
}
|
||||
signal_handlers_options_ = SignalHandlerOptions::None;
|
||||
RCLCPP_DEBUG(get_logger(), "SignalHandler::uninstall(): notifying deferred signal handler");
|
||||
notify_signal_handler();
|
||||
signal_handler_thread_.join();
|
||||
@@ -151,98 +214,57 @@ SignalHandler::~SignalHandler()
|
||||
} catch (const std::exception & exc) {
|
||||
RCLCPP_ERROR(
|
||||
get_logger(),
|
||||
"caught %s exception when uninstalling the sigint handler in rclcpp::~SignalHandler: %s",
|
||||
"caught %s exception when uninstalling signal handlers in rclcpp::~SignalHandler: %s",
|
||||
rmw::impl::cpp::demangle(exc).c_str(), exc.what());
|
||||
} catch (...) {
|
||||
RCLCPP_ERROR(
|
||||
get_logger(),
|
||||
"caught unknown exception when uninstalling the sigint handler in rclcpp::~SignalHandler");
|
||||
"caught unknown exception when uninstalling signal handlers in rclcpp::~SignalHandler");
|
||||
}
|
||||
}
|
||||
|
||||
SignalHandler::signal_handler_type
|
||||
SignalHandler::set_signal_handler(
|
||||
int signal_value,
|
||||
const SignalHandler::signal_handler_type & signal_handler)
|
||||
SignalHandler::get_old_signal_handler(int signum)
|
||||
{
|
||||
bool signal_handler_install_failed;
|
||||
SignalHandler::signal_handler_type old_signal_handler;
|
||||
#if defined(RCLCPP_HAS_SIGACTION)
|
||||
ssize_t ret = sigaction(signal_value, &signal_handler, &old_signal_handler);
|
||||
signal_handler_install_failed = (ret == -1);
|
||||
#else
|
||||
old_signal_handler = std::signal(signal_value, signal_handler);
|
||||
signal_handler_install_failed = (old_signal_handler == SIG_ERR);
|
||||
#endif
|
||||
if (signal_handler_install_failed) {
|
||||
char error_string[1024];
|
||||
rcutils_strerror(error_string, sizeof(error_string));
|
||||
auto msg =
|
||||
"Failed to set SIGINT signal handler (" + std::to_string(errno) + "): " + error_string;
|
||||
throw std::runtime_error(msg);
|
||||
if (SIGINT == signum) {
|
||||
return old_sigint_handler_;
|
||||
} else if (SIGTERM == signum) {
|
||||
return old_sigterm_handler_;
|
||||
}
|
||||
|
||||
return old_signal_handler;
|
||||
#if defined(RCLCPP_HAS_SIGACTION)
|
||||
SignalHandler::signal_handler_type ret;
|
||||
memset(&ret, 0, sizeof(ret));
|
||||
sigemptyset(&ret.sa_mask);
|
||||
ret.sa_handler = SIG_DFL;
|
||||
return ret;
|
||||
#else
|
||||
return SIG_DFL;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
SignalHandler::signal_handler_common()
|
||||
{
|
||||
signal_received_.store(true);
|
||||
auto & instance = SignalHandler::get_global_signal_handler();
|
||||
instance.signal_received_.store(true);
|
||||
RCLCPP_DEBUG(
|
||||
get_logger(),
|
||||
"signal_handler(): SIGINT received, notifying deferred signal handler");
|
||||
notify_signal_handler();
|
||||
"signal_handler(): notifying deferred signal handler");
|
||||
instance.notify_signal_handler();
|
||||
}
|
||||
|
||||
#if defined(RCLCPP_HAS_SIGACTION)
|
||||
void
|
||||
SignalHandler::signal_handler(int signal_value, siginfo_t * siginfo, void * context)
|
||||
{
|
||||
RCLCPP_INFO(get_logger(), "signal_handler(signal_value=%d)", signal_value);
|
||||
|
||||
if (old_signal_handler_.sa_flags & SA_SIGINFO) {
|
||||
if (old_signal_handler_.sa_sigaction != NULL) {
|
||||
old_signal_handler_.sa_sigaction(signal_value, siginfo, context);
|
||||
}
|
||||
} else {
|
||||
if (
|
||||
old_signal_handler_.sa_handler != NULL && // Is set
|
||||
old_signal_handler_.sa_handler != SIG_DFL && // Is not default
|
||||
old_signal_handler_.sa_handler != SIG_IGN) // Is not ignored
|
||||
{
|
||||
old_signal_handler_.sa_handler(signal_value);
|
||||
}
|
||||
}
|
||||
|
||||
signal_handler_common();
|
||||
}
|
||||
#else
|
||||
void
|
||||
SignalHandler::signal_handler(int signal_value)
|
||||
{
|
||||
RCLCPP_INFO(get_logger(), "signal_handler(signal_value=%d)", signal_value);
|
||||
|
||||
if (old_signal_handler_) {
|
||||
old_signal_handler_(signal_value);
|
||||
}
|
||||
|
||||
signal_handler_common();
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
SignalHandler::deferred_signal_handler()
|
||||
{
|
||||
while (true) {
|
||||
if (signal_received_.exchange(false)) {
|
||||
RCLCPP_DEBUG(get_logger(), "deferred_signal_handler(): SIGINT received, shutting down");
|
||||
RCLCPP_DEBUG(get_logger(), "deferred_signal_handler(): shutting down");
|
||||
for (auto context_ptr : rclcpp::get_contexts()) {
|
||||
if (context_ptr->get_init_options().shutdown_on_sigint) {
|
||||
if (context_ptr->get_init_options().shutdown_on_signal) {
|
||||
RCLCPP_DEBUG(
|
||||
get_logger(),
|
||||
"deferred_signal_handler(): "
|
||||
"shutting down rclcpp::Context @ %p, because it had shutdown_on_sigint == true",
|
||||
"shutting down rclcpp::Context @ %p, because it had shutdown_on_signal == true",
|
||||
static_cast<void *>(context_ptr.get()));
|
||||
context_ptr->shutdown("signal handler");
|
||||
}
|
||||
@@ -252,9 +274,11 @@ SignalHandler::deferred_signal_handler()
|
||||
RCLCPP_DEBUG(get_logger(), "deferred_signal_handler(): signal handling uninstalled");
|
||||
break;
|
||||
}
|
||||
RCLCPP_DEBUG(get_logger(), "deferred_signal_handler(): waiting for SIGINT or uninstall");
|
||||
RCLCPP_DEBUG(
|
||||
get_logger(), "deferred_signal_handler(): waiting for SIGINT/SIGTERM or uninstall");
|
||||
wait_for_signal();
|
||||
RCLCPP_DEBUG(get_logger(), "deferred_signal_handler(): woken up due to SIGINT or uninstall");
|
||||
RCLCPP_DEBUG(
|
||||
get_logger(), "deferred_signal_handler(): woken up due to SIGINT/SIGTERM or uninstall");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -356,3 +380,9 @@ SignalHandler::notify_signal_handler() noexcept
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
rclcpp::SignalHandlerOptions
|
||||
SignalHandler::get_current_signal_handler_options()
|
||||
{
|
||||
return signal_handlers_options_;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <thread>
|
||||
|
||||
#include "rclcpp/logging.hpp"
|
||||
#include "rclcpp/utilities.hpp"
|
||||
|
||||
// includes for semaphore notification code
|
||||
#if defined(_WIN32)
|
||||
@@ -39,14 +40,14 @@
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
/// Responsible for manaaging the SIGINT signal handling.
|
||||
/// Responsible for managing the SIGINT/SIGTERM signal handling.
|
||||
/**
|
||||
* This class is responsible for:
|
||||
*
|
||||
* - installing the signal handler for SIGINT
|
||||
* - uninstalling the signal handler for SIGINT
|
||||
* - creating a thread to execute "on sigint" work outside of the signal handler
|
||||
* - safely notifying the dedicated signal handling thread when receiving SIGINT
|
||||
* - installing the signal handler for SIGINT/SIGTERM
|
||||
* - uninstalling the signal handler for SIGINT/SIGTERM
|
||||
* - creating a thread to execute "on signal" work outside of the signal handler
|
||||
* - safely notifying the dedicated signal handling thread when receiving SIGINT/SIGTERM
|
||||
* - implementation of all of the signal handling work, like shutting down contexts
|
||||
*
|
||||
* \internal
|
||||
@@ -64,15 +65,18 @@ public:
|
||||
rclcpp::Logger &
|
||||
get_logger();
|
||||
|
||||
/// Install the signal handler for SIGINT and start the dedicated signal handling thread.
|
||||
/// Install the signal handler for SIGINT/SIGTERM and start the dedicated signal handling thread.
|
||||
/**
|
||||
* Also stores the current signal handler to be called on SIGINT and to
|
||||
* Also stores the current signal handler to be called on signal and to
|
||||
* restore when uninstalling this signal handler.
|
||||
*
|
||||
* \param signal_handler_options option to indicate which signal handlers should be installed.
|
||||
*/
|
||||
bool
|
||||
install();
|
||||
install(SignalHandlerOptions signal_handler_options = SignalHandlerOptions::All);
|
||||
|
||||
/// Uninstall the signal handler for SIGINT and join the dedicated singal handling thread.
|
||||
/// Uninstall the signal handler for SIGINT/SIGTERM and join the dedicated singal handling
|
||||
/// thread.
|
||||
/**
|
||||
* Also restores the previous signal handler.
|
||||
*/
|
||||
@@ -83,26 +87,34 @@ public:
|
||||
bool
|
||||
is_installed();
|
||||
|
||||
/// Get the current signal handler options.
|
||||
/**
|
||||
* If no signal handler is installed, SignalHandlerOptions::None is returned.
|
||||
*/
|
||||
rclcpp::SignalHandlerOptions
|
||||
get_current_signal_handler_options();
|
||||
|
||||
private:
|
||||
SignalHandler() = default;
|
||||
|
||||
~SignalHandler();
|
||||
|
||||
/// Signal handler type, platform dependent.
|
||||
#if defined(RCLCPP_HAS_SIGACTION)
|
||||
using signal_handler_type = struct sigaction;
|
||||
#else
|
||||
using signal_handler_type = void (*)(int);
|
||||
#endif
|
||||
// POSIX signal handler structure storage for the existing signal handler.
|
||||
static SignalHandler::signal_handler_type old_signal_handler_;
|
||||
|
||||
/// Set the signal handler function.
|
||||
static
|
||||
SignalHandler::signal_handler_type
|
||||
set_signal_handler(int signal_value, const SignalHandler::signal_handler_type & signal_handler);
|
||||
|
||||
SignalHandler() = default;
|
||||
|
||||
~SignalHandler();
|
||||
|
||||
SignalHandler(const SignalHandler &) = delete;
|
||||
SignalHandler(SignalHandler &&) = delete;
|
||||
SignalHandler &
|
||||
operator=(const SignalHandler &) = delete;
|
||||
SignalHandler &&
|
||||
operator=(SignalHandler &&) = delete;
|
||||
|
||||
/// Common signal handler code between sigaction and non-sigaction versions.
|
||||
static
|
||||
void
|
||||
signal_handler_common();
|
||||
|
||||
@@ -127,7 +139,6 @@ private:
|
||||
* This must be called before wait_for_signal() or notify_signal_handler().
|
||||
* This is not thread-safe.
|
||||
*/
|
||||
static
|
||||
void
|
||||
setup_wait_for_signal();
|
||||
|
||||
@@ -137,7 +148,6 @@ private:
|
||||
*
|
||||
* This is not thread-safe.
|
||||
*/
|
||||
static
|
||||
void
|
||||
teardown_wait_for_signal() noexcept;
|
||||
|
||||
@@ -147,7 +157,6 @@ private:
|
||||
*
|
||||
* This is not thread-safe.
|
||||
*/
|
||||
static
|
||||
void
|
||||
wait_for_signal();
|
||||
|
||||
@@ -158,29 +167,45 @@ private:
|
||||
*
|
||||
* This is thread-safe.
|
||||
*/
|
||||
static
|
||||
void
|
||||
notify_signal_handler() noexcept;
|
||||
|
||||
static
|
||||
signal_handler_type
|
||||
set_signal_handler(
|
||||
int signal_value,
|
||||
const signal_handler_type & signal_handler);
|
||||
|
||||
signal_handler_type
|
||||
get_old_signal_handler(int signum);
|
||||
|
||||
rclcpp::SignalHandlerOptions signal_handlers_options_ = rclcpp::SignalHandlerOptions::None;
|
||||
|
||||
signal_handler_type old_sigint_handler_;
|
||||
signal_handler_type old_sigterm_handler_;
|
||||
|
||||
// logger instance
|
||||
rclcpp::Logger logger_ = rclcpp::get_logger("rclcpp");
|
||||
|
||||
// Whether or not a signal has been received.
|
||||
static std::atomic_bool signal_received_;
|
||||
std::atomic_bool signal_received_ = false;
|
||||
// A thread to which singal handling tasks are deferred.
|
||||
std::thread signal_handler_thread_;
|
||||
|
||||
// A mutex used to synchronize the install() and uninstall() methods.
|
||||
std::mutex install_mutex_;
|
||||
// Whether or not the signal handler has been installed.
|
||||
std::atomic_bool installed_{false};
|
||||
std::atomic_bool installed_ = false;
|
||||
|
||||
// Whether or not the semaphore for wait_for_signal is setup.
|
||||
static std::atomic_bool wait_for_signal_is_setup_;
|
||||
std::atomic_bool wait_for_signal_is_setup_;
|
||||
// Storage for the wait_for_signal semaphore.
|
||||
#if defined(_WIN32)
|
||||
static HANDLE signal_handler_sem_;
|
||||
HANDLE signal_handler_sem_;
|
||||
#elif defined(__APPLE__)
|
||||
static dispatch_semaphore_t signal_handler_sem_;
|
||||
dispatch_semaphore_t signal_handler_sem_;
|
||||
#else // posix
|
||||
static sem_t signal_handler_sem_;
|
||||
sem_t signal_handler_sem_;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -13,16 +13,14 @@
|
||||
// limitations under the License.
|
||||
|
||||
#include "rclcpp/experimental/subscription_intra_process_base.hpp"
|
||||
#include "rclcpp/detail/add_guard_condition_to_rcl_wait_set.hpp"
|
||||
|
||||
using rclcpp::experimental::SubscriptionIntraProcessBase;
|
||||
|
||||
bool
|
||||
void
|
||||
SubscriptionIntraProcessBase::add_to_wait_set(rcl_wait_set_t * wait_set)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(reentrant_mutex_);
|
||||
|
||||
rcl_ret_t ret = rcl_wait_set_add_guard_condition(wait_set, &gc_, NULL);
|
||||
return RCL_RET_OK == ret;
|
||||
detail::add_guard_condition_to_rcl_wait_set(*wait_set, gc_);
|
||||
}
|
||||
|
||||
const char *
|
||||
@@ -31,7 +29,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_;
|
||||
|
||||
@@ -33,29 +33,499 @@
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
class TimeSource::ClocksState : public std::enable_shared_from_this<ClocksState>
|
||||
{
|
||||
public:
|
||||
ClocksState()
|
||||
: logger_(rclcpp::get_logger("rclcpp"))
|
||||
{
|
||||
}
|
||||
|
||||
// An internal method to use in the clock callback that iterates and enables all clocks
|
||||
void enable_ros_time()
|
||||
{
|
||||
if (ros_time_active_) {
|
||||
// already enabled no-op
|
||||
return;
|
||||
}
|
||||
|
||||
// Local storage
|
||||
ros_time_active_ = true;
|
||||
|
||||
// Update all attached clocks to zero or last recorded time
|
||||
std::lock_guard<std::mutex> guard(clock_list_lock_);
|
||||
auto time_msg = std::make_shared<builtin_interfaces::msg::Time>();
|
||||
if (last_msg_set_) {
|
||||
time_msg = std::make_shared<builtin_interfaces::msg::Time>(last_msg_set_->clock);
|
||||
}
|
||||
for (auto it = associated_clocks_.begin(); it != associated_clocks_.end(); ++it) {
|
||||
set_clock(time_msg, true, *it);
|
||||
}
|
||||
}
|
||||
|
||||
// An internal method to use in the clock callback that iterates and disables all clocks
|
||||
void disable_ros_time()
|
||||
{
|
||||
if (!ros_time_active_) {
|
||||
// already disabled no-op
|
||||
return;
|
||||
}
|
||||
|
||||
// Local storage
|
||||
ros_time_active_ = false;
|
||||
|
||||
// Update all attached clocks
|
||||
std::lock_guard<std::mutex> guard(clock_list_lock_);
|
||||
for (auto it = associated_clocks_.begin(); it != associated_clocks_.end(); ++it) {
|
||||
auto msg = std::make_shared<builtin_interfaces::msg::Time>();
|
||||
set_clock(msg, false, *it);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if ROS time is active
|
||||
bool is_ros_time_active() const
|
||||
{
|
||||
return ros_time_active_;
|
||||
}
|
||||
|
||||
// Attach a clock
|
||||
void attachClock(rclcpp::Clock::SharedPtr clock)
|
||||
{
|
||||
if (clock->get_clock_type() != RCL_ROS_TIME) {
|
||||
throw std::invalid_argument("Cannot attach clock to a time source that's not a ROS clock");
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> guard(clock_list_lock_);
|
||||
associated_clocks_.push_back(clock);
|
||||
// Set the clock to zero unless there's a recently received message
|
||||
auto time_msg = std::make_shared<builtin_interfaces::msg::Time>();
|
||||
if (last_msg_set_) {
|
||||
time_msg = std::make_shared<builtin_interfaces::msg::Time>(last_msg_set_->clock);
|
||||
}
|
||||
set_clock(time_msg, ros_time_active_, clock);
|
||||
}
|
||||
|
||||
// Detach a clock
|
||||
void detachClock(rclcpp::Clock::SharedPtr clock)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(clock_list_lock_);
|
||||
auto result = std::find(associated_clocks_.begin(), associated_clocks_.end(), clock);
|
||||
if (result != associated_clocks_.end()) {
|
||||
associated_clocks_.erase(result);
|
||||
} else {
|
||||
RCLCPP_ERROR(logger_, "failed to remove clock");
|
||||
}
|
||||
}
|
||||
|
||||
// Internal helper function used inside iterators
|
||||
static void set_clock(
|
||||
const builtin_interfaces::msg::Time::SharedPtr msg,
|
||||
bool set_ros_time_enabled,
|
||||
rclcpp::Clock::SharedPtr clock)
|
||||
{
|
||||
std::lock_guard<std::mutex> clock_guard(clock->get_clock_mutex());
|
||||
|
||||
// Do change
|
||||
if (!set_ros_time_enabled && clock->ros_time_is_active()) {
|
||||
auto ret = rcl_disable_ros_time_override(clock->get_clock_handle());
|
||||
if (ret != RCL_RET_OK) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(
|
||||
ret, "Failed to disable ros_time_override_status");
|
||||
}
|
||||
} else if (set_ros_time_enabled && !clock->ros_time_is_active()) {
|
||||
auto ret = rcl_enable_ros_time_override(clock->get_clock_handle());
|
||||
if (ret != RCL_RET_OK) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(
|
||||
ret, "Failed to enable ros_time_override_status");
|
||||
}
|
||||
}
|
||||
|
||||
auto ret = rcl_set_ros_time_override(
|
||||
clock->get_clock_handle(),
|
||||
rclcpp::Time(*msg).nanoseconds());
|
||||
if (ret != RCL_RET_OK) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(
|
||||
ret, "Failed to set ros_time_override_status");
|
||||
}
|
||||
}
|
||||
|
||||
// Internal helper function
|
||||
void set_all_clocks(
|
||||
const builtin_interfaces::msg::Time::SharedPtr msg,
|
||||
bool set_ros_time_enabled)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(clock_list_lock_);
|
||||
for (auto it = associated_clocks_.begin(); it != associated_clocks_.end(); ++it) {
|
||||
set_clock(msg, set_ros_time_enabled, *it);
|
||||
}
|
||||
}
|
||||
|
||||
// Cache the last clock message received
|
||||
void cache_last_msg(std::shared_ptr<const rosgraph_msgs::msg::Clock> msg)
|
||||
{
|
||||
last_msg_set_ = msg;
|
||||
}
|
||||
|
||||
private:
|
||||
// Store (and update on node attach) logger for logging.
|
||||
Logger logger_;
|
||||
|
||||
// A lock to protect iterating the associated_clocks_ field.
|
||||
std::mutex clock_list_lock_;
|
||||
// A vector to store references to associated clocks.
|
||||
std::vector<rclcpp::Clock::SharedPtr> associated_clocks_;
|
||||
|
||||
// Local storage of validity of ROS time
|
||||
// This is needed when new clocks are added.
|
||||
bool ros_time_active_{false};
|
||||
// Last set message to be passed to newly registered clocks
|
||||
std::shared_ptr<const rosgraph_msgs::msg::Clock> last_msg_set_;
|
||||
};
|
||||
|
||||
class TimeSource::NodeState : public std::enable_shared_from_this<NodeState>
|
||||
{
|
||||
public:
|
||||
NodeState(std::weak_ptr<ClocksState> clocks_state, const rclcpp::QoS & qos, bool use_clock_thread)
|
||||
: clocks_state_(std::move(clocks_state)),
|
||||
use_clock_thread_(use_clock_thread),
|
||||
logger_(rclcpp::get_logger("rclcpp")),
|
||||
qos_(qos)
|
||||
{
|
||||
}
|
||||
|
||||
~NodeState()
|
||||
{
|
||||
if (
|
||||
node_base_ || node_topics_ || node_graph_ || node_services_ ||
|
||||
node_logging_ || node_clock_ || node_parameters_)
|
||||
{
|
||||
detachNode();
|
||||
}
|
||||
}
|
||||
|
||||
// Check if a clock thread will be used
|
||||
bool get_use_clock_thread()
|
||||
{
|
||||
return use_clock_thread_;
|
||||
}
|
||||
|
||||
// Set whether a clock thread will be used
|
||||
void set_use_clock_thread(bool use_clock_thread)
|
||||
{
|
||||
use_clock_thread_ = use_clock_thread;
|
||||
}
|
||||
|
||||
// Check if the clock thread is joinable
|
||||
bool clock_thread_is_joinable()
|
||||
{
|
||||
return clock_executor_thread_.joinable();
|
||||
}
|
||||
|
||||
// Attach a node to this time source
|
||||
void attachNode(
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base_interface,
|
||||
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics_interface,
|
||||
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph_interface,
|
||||
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services_interface,
|
||||
rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging_interface,
|
||||
rclcpp::node_interfaces::NodeClockInterface::SharedPtr node_clock_interface,
|
||||
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters_interface)
|
||||
{
|
||||
node_base_ = node_base_interface;
|
||||
node_topics_ = node_topics_interface;
|
||||
node_graph_ = node_graph_interface;
|
||||
node_services_ = node_services_interface;
|
||||
node_logging_ = node_logging_interface;
|
||||
node_clock_ = node_clock_interface;
|
||||
node_parameters_ = node_parameters_interface;
|
||||
// TODO(tfoote): Update QOS
|
||||
|
||||
logger_ = node_logging_->get_logger();
|
||||
|
||||
// Though this defaults to false, it can be overridden by initial parameter values for the
|
||||
// node, which may be given by the user at the node's construction or even by command-line
|
||||
// arguments.
|
||||
rclcpp::ParameterValue use_sim_time_param;
|
||||
const std::string use_sim_time_name = "use_sim_time";
|
||||
if (!node_parameters_->has_parameter(use_sim_time_name)) {
|
||||
use_sim_time_param = node_parameters_->declare_parameter(
|
||||
use_sim_time_name,
|
||||
rclcpp::ParameterValue(false),
|
||||
rcl_interfaces::msg::ParameterDescriptor());
|
||||
} else {
|
||||
use_sim_time_param = node_parameters_->get_parameter(use_sim_time_name).get_parameter_value();
|
||||
}
|
||||
if (use_sim_time_param.get_type() == rclcpp::PARAMETER_BOOL) {
|
||||
if (use_sim_time_param.get<bool>()) {
|
||||
parameter_state_ = SET_TRUE;
|
||||
// There should be no way to call this attachNode when the clocks_state_ pointer is not
|
||||
// valid because it means the TimeSource is being destroyed
|
||||
if (auto clocks_state_ptr = clocks_state_.lock()) {
|
||||
clocks_state_ptr->enable_ros_time();
|
||||
create_clock_sub();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
RCLCPP_ERROR(
|
||||
logger_, "Invalid type '%s' for parameter 'use_sim_time', should be 'bool'",
|
||||
rclcpp::to_string(use_sim_time_param.get_type()).c_str());
|
||||
}
|
||||
sim_time_cb_handler_ = node_parameters_->add_on_set_parameters_callback(
|
||||
[use_sim_time_name](const std::vector<rclcpp::Parameter> & parameters) {
|
||||
rcl_interfaces::msg::SetParametersResult result;
|
||||
result.successful = true;
|
||||
for (const auto & parameter : parameters) {
|
||||
if (
|
||||
parameter.get_name() == use_sim_time_name &&
|
||||
parameter.get_type() != rclcpp::PARAMETER_BOOL)
|
||||
{
|
||||
result.successful = false;
|
||||
result.reason = "'" + use_sim_time_name + "' must be a bool";
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
});
|
||||
|
||||
// TODO(tfoote) use parameters interface not subscribe to events via topic ticketed #609
|
||||
parameter_subscription_ = rclcpp::AsyncParametersClient::on_parameter_event(
|
||||
node_topics_,
|
||||
[state = std::weak_ptr<NodeState>(this->shared_from_this())](
|
||||
std::shared_ptr<const rcl_interfaces::msg::ParameterEvent> event) {
|
||||
if (auto state_ptr = state.lock()) {
|
||||
state_ptr->on_parameter_event(event);
|
||||
}
|
||||
// Do nothing if the pointer could not be locked because it means the TimeSource is now
|
||||
// without an attached node
|
||||
});
|
||||
}
|
||||
|
||||
// Detach the attached node
|
||||
void detachNode()
|
||||
{
|
||||
// There should be no way to call detachNode when the clocks_state_ pointer is not valid
|
||||
// because it means the TimeSource is being destroyed
|
||||
if (auto clocks_state_ptr = clocks_state_.lock()) {
|
||||
clocks_state_ptr->disable_ros_time();
|
||||
}
|
||||
destroy_clock_sub();
|
||||
parameter_subscription_.reset();
|
||||
node_base_.reset();
|
||||
node_topics_.reset();
|
||||
node_graph_.reset();
|
||||
node_services_.reset();
|
||||
node_logging_.reset();
|
||||
node_clock_.reset();
|
||||
if (sim_time_cb_handler_ && node_parameters_) {
|
||||
node_parameters_->remove_on_set_parameters_callback(sim_time_cb_handler_.get());
|
||||
}
|
||||
sim_time_cb_handler_.reset();
|
||||
node_parameters_.reset();
|
||||
}
|
||||
|
||||
private:
|
||||
std::weak_ptr<ClocksState> clocks_state_;
|
||||
|
||||
// Dedicated thread for clock subscription.
|
||||
bool use_clock_thread_;
|
||||
std::thread clock_executor_thread_;
|
||||
|
||||
// Preserve the node reference
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base_;
|
||||
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics_;
|
||||
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph_;
|
||||
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services_;
|
||||
rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging_;
|
||||
rclcpp::node_interfaces::NodeClockInterface::SharedPtr node_clock_;
|
||||
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters_;
|
||||
|
||||
// Store (and update on node attach) logger for logging.
|
||||
Logger logger_;
|
||||
|
||||
// QoS of the clock subscription.
|
||||
rclcpp::QoS qos_;
|
||||
|
||||
// The subscription for the clock callback
|
||||
using MessageT = rosgraph_msgs::msg::Clock;
|
||||
using Alloc = std::allocator<void>;
|
||||
using SubscriptionT = rclcpp::Subscription<MessageT, Alloc>;
|
||||
std::shared_ptr<SubscriptionT> clock_subscription_{nullptr};
|
||||
std::mutex clock_sub_lock_;
|
||||
rclcpp::CallbackGroup::SharedPtr clock_callback_group_;
|
||||
rclcpp::executors::SingleThreadedExecutor::SharedPtr clock_executor_;
|
||||
std::promise<void> cancel_clock_executor_promise_;
|
||||
|
||||
// The clock callback itself
|
||||
void clock_cb(std::shared_ptr<const rosgraph_msgs::msg::Clock> msg)
|
||||
{
|
||||
auto clocks_state_ptr = clocks_state_.lock();
|
||||
if (!clocks_state_ptr) {
|
||||
// The clock_state_ pointer is no longer valid, implying that the TimeSource object is being
|
||||
// destroyed, so do nothing
|
||||
return;
|
||||
}
|
||||
if (!clocks_state_ptr->is_ros_time_active() && SET_TRUE == this->parameter_state_) {
|
||||
clocks_state_ptr->enable_ros_time();
|
||||
}
|
||||
// Cache the last message in case a new clock is attached.
|
||||
clocks_state_ptr->cache_last_msg(msg);
|
||||
auto time_msg = std::make_shared<builtin_interfaces::msg::Time>(msg->clock);
|
||||
|
||||
if (SET_TRUE == this->parameter_state_) {
|
||||
clocks_state_ptr->set_all_clocks(time_msg, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Create the subscription for the clock topic
|
||||
void create_clock_sub()
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(clock_sub_lock_);
|
||||
if (clock_subscription_) {
|
||||
// Subscription already created.
|
||||
return;
|
||||
}
|
||||
|
||||
rclcpp::SubscriptionOptions options;
|
||||
options.qos_overriding_options = rclcpp::QosOverridingOptions(
|
||||
{
|
||||
rclcpp::QosPolicyKind::Depth,
|
||||
rclcpp::QosPolicyKind::Durability,
|
||||
rclcpp::QosPolicyKind::History,
|
||||
rclcpp::QosPolicyKind::Reliability,
|
||||
});
|
||||
|
||||
if (use_clock_thread_) {
|
||||
clock_callback_group_ = node_base_->create_callback_group(
|
||||
rclcpp::CallbackGroupType::MutuallyExclusive,
|
||||
false
|
||||
);
|
||||
options.callback_group = clock_callback_group_;
|
||||
rclcpp::ExecutorOptions exec_options;
|
||||
exec_options.context = node_base_->get_context();
|
||||
clock_executor_ =
|
||||
std::make_shared<rclcpp::executors::SingleThreadedExecutor>(exec_options);
|
||||
if (!clock_executor_thread_.joinable()) {
|
||||
cancel_clock_executor_promise_ = std::promise<void>{};
|
||||
clock_executor_thread_ = std::thread(
|
||||
[this]() {
|
||||
auto future = cancel_clock_executor_promise_.get_future();
|
||||
clock_executor_->add_callback_group(clock_callback_group_, node_base_);
|
||||
clock_executor_->spin_until_future_complete(future);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
clock_subscription_ = rclcpp::create_subscription<rosgraph_msgs::msg::Clock>(
|
||||
node_parameters_,
|
||||
node_topics_,
|
||||
"/clock",
|
||||
qos_,
|
||||
[state = std::weak_ptr<NodeState>(this->shared_from_this())](
|
||||
std::shared_ptr<const rosgraph_msgs::msg::Clock> msg) {
|
||||
if (auto state_ptr = state.lock()) {
|
||||
state_ptr->clock_cb(msg);
|
||||
}
|
||||
// Do nothing if the pointer could not be locked because it means the TimeSource is now
|
||||
// without an attached node
|
||||
},
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
// Destroy the subscription for the clock topic
|
||||
void destroy_clock_sub()
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(clock_sub_lock_);
|
||||
if (clock_executor_thread_.joinable()) {
|
||||
cancel_clock_executor_promise_.set_value();
|
||||
clock_executor_->cancel();
|
||||
clock_executor_thread_.join();
|
||||
clock_executor_->remove_callback_group(clock_callback_group_);
|
||||
}
|
||||
clock_subscription_.reset();
|
||||
}
|
||||
|
||||
// Parameter Event subscription
|
||||
using ParamMessageT = rcl_interfaces::msg::ParameterEvent;
|
||||
using ParamSubscriptionT = rclcpp::Subscription<ParamMessageT, Alloc>;
|
||||
std::shared_ptr<ParamSubscriptionT> parameter_subscription_;
|
||||
|
||||
// Callback for parameter updates
|
||||
void on_parameter_event(std::shared_ptr<const rcl_interfaces::msg::ParameterEvent> event)
|
||||
{
|
||||
auto clocks_state_ptr = clocks_state_.lock();
|
||||
if (!clocks_state_ptr) {
|
||||
// The clock_state_ pointer is no longer valid, implying that the TimeSource object is being
|
||||
// destroyed, so do nothing
|
||||
return;
|
||||
}
|
||||
// Filter out events on 'use_sim_time' parameter instances in other nodes.
|
||||
if (event->node != node_base_->get_fully_qualified_name()) {
|
||||
return;
|
||||
}
|
||||
// Filter for only 'use_sim_time' being added or changed.
|
||||
rclcpp::ParameterEventsFilter filter(event, {"use_sim_time"},
|
||||
{rclcpp::ParameterEventsFilter::EventType::NEW,
|
||||
rclcpp::ParameterEventsFilter::EventType::CHANGED});
|
||||
for (auto & it : filter.get_events()) {
|
||||
if (it.second->value.type != ParameterType::PARAMETER_BOOL) {
|
||||
RCLCPP_ERROR(logger_, "use_sim_time parameter cannot be set to anything but a bool");
|
||||
continue;
|
||||
}
|
||||
if (it.second->value.bool_value) {
|
||||
parameter_state_ = SET_TRUE;
|
||||
clocks_state_ptr->enable_ros_time();
|
||||
create_clock_sub();
|
||||
} else {
|
||||
parameter_state_ = SET_FALSE;
|
||||
clocks_state_ptr->disable_ros_time();
|
||||
destroy_clock_sub();
|
||||
}
|
||||
}
|
||||
// Handle the case that use_sim_time was deleted.
|
||||
rclcpp::ParameterEventsFilter deleted(event, {"use_sim_time"},
|
||||
{rclcpp::ParameterEventsFilter::EventType::DELETED});
|
||||
for (auto & it : deleted.get_events()) {
|
||||
(void) it; // if there is a match it's already matched, don't bother reading it.
|
||||
// If the parameter is deleted mark it as unset but dont' change state.
|
||||
parameter_state_ = UNSET;
|
||||
}
|
||||
}
|
||||
|
||||
// An enum to hold the parameter state
|
||||
enum UseSimTimeParameterState {UNSET, SET_TRUE, SET_FALSE};
|
||||
UseSimTimeParameterState parameter_state_;
|
||||
|
||||
// A handler for the use_sim_time parameter callback.
|
||||
rclcpp::node_interfaces::OnSetParametersCallbackHandle::SharedPtr sim_time_cb_handler_{nullptr};
|
||||
};
|
||||
|
||||
TimeSource::TimeSource(
|
||||
std::shared_ptr<rclcpp::Node> node,
|
||||
const rclcpp::QoS & qos,
|
||||
bool use_clock_thread)
|
||||
: use_clock_thread_(use_clock_thread),
|
||||
logger_(rclcpp::get_logger("rclcpp")),
|
||||
qos_(qos)
|
||||
: constructed_use_clock_thread_(use_clock_thread),
|
||||
constructed_qos_(qos)
|
||||
{
|
||||
this->attachNode(node);
|
||||
clocks_state_ = std::make_shared<ClocksState>();
|
||||
node_state_ = std::make_shared<NodeState>(clocks_state_->weak_from_this(), qos, use_clock_thread);
|
||||
attachNode(node);
|
||||
}
|
||||
|
||||
TimeSource::TimeSource(
|
||||
const rclcpp::QoS & qos,
|
||||
bool use_clock_thread)
|
||||
: use_clock_thread_(use_clock_thread),
|
||||
logger_(rclcpp::get_logger("rclcpp")),
|
||||
qos_(qos)
|
||||
: constructed_use_clock_thread_(use_clock_thread),
|
||||
constructed_qos_(qos)
|
||||
{
|
||||
clocks_state_ = std::make_shared<ClocksState>();
|
||||
node_state_ = std::make_shared<NodeState>(clocks_state_->weak_from_this(), qos, use_clock_thread);
|
||||
}
|
||||
|
||||
void TimeSource::attachNode(rclcpp::Node::SharedPtr node)
|
||||
{
|
||||
use_clock_thread_ = node->get_node_options().use_clock_thread();
|
||||
node_state_->set_use_clock_thread(node->get_node_options().use_clock_thread());
|
||||
attachNode(
|
||||
node->get_node_base_interface(),
|
||||
node->get_node_topics_interface(),
|
||||
@@ -75,298 +545,52 @@ void TimeSource::attachNode(
|
||||
rclcpp::node_interfaces::NodeClockInterface::SharedPtr node_clock_interface,
|
||||
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters_interface)
|
||||
{
|
||||
node_base_ = node_base_interface;
|
||||
node_topics_ = node_topics_interface;
|
||||
node_graph_ = node_graph_interface;
|
||||
node_services_ = node_services_interface;
|
||||
node_logging_ = node_logging_interface;
|
||||
node_clock_ = node_clock_interface;
|
||||
node_parameters_ = node_parameters_interface;
|
||||
// TODO(tfoote): Update QOS
|
||||
|
||||
logger_ = node_logging_->get_logger();
|
||||
|
||||
// Though this defaults to false, it can be overridden by initial parameter values for the node,
|
||||
// which may be given by the user at the node's construction or even by command-line arguments.
|
||||
rclcpp::ParameterValue use_sim_time_param;
|
||||
const std::string use_sim_time_name = "use_sim_time";
|
||||
if (!node_parameters_->has_parameter(use_sim_time_name)) {
|
||||
use_sim_time_param = node_parameters_->declare_parameter(
|
||||
use_sim_time_name,
|
||||
rclcpp::ParameterValue(false),
|
||||
rcl_interfaces::msg::ParameterDescriptor());
|
||||
} else {
|
||||
use_sim_time_param = node_parameters_->get_parameter(use_sim_time_name).get_parameter_value();
|
||||
}
|
||||
if (use_sim_time_param.get_type() == rclcpp::PARAMETER_BOOL) {
|
||||
if (use_sim_time_param.get<bool>()) {
|
||||
parameter_state_ = SET_TRUE;
|
||||
enable_ros_time();
|
||||
create_clock_sub();
|
||||
}
|
||||
} else {
|
||||
RCLCPP_ERROR(
|
||||
logger_, "Invalid type '%s' for parameter 'use_sim_time', should be 'bool'",
|
||||
rclcpp::to_string(use_sim_time_param.get_type()).c_str());
|
||||
}
|
||||
sim_time_cb_handler_ = node_parameters_->add_on_set_parameters_callback(
|
||||
[use_sim_time_name](const std::vector<rclcpp::Parameter> & parameters) {
|
||||
rcl_interfaces::msg::SetParametersResult result;
|
||||
result.successful = true;
|
||||
for (const auto & parameter : parameters) {
|
||||
if (
|
||||
parameter.get_name() == use_sim_time_name &&
|
||||
parameter.get_type() != rclcpp::PARAMETER_BOOL)
|
||||
{
|
||||
result.successful = false;
|
||||
result.reason = "'" + use_sim_time_name + "' must be a bool";
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
});
|
||||
|
||||
// TODO(tfoote) use parameters interface not subscribe to events via topic ticketed #609
|
||||
parameter_subscription_ = rclcpp::AsyncParametersClient::on_parameter_event(
|
||||
node_topics_,
|
||||
std::bind(&TimeSource::on_parameter_event, this, std::placeholders::_1));
|
||||
node_state_->attachNode(
|
||||
std::move(node_base_interface),
|
||||
std::move(node_topics_interface),
|
||||
std::move(node_graph_interface),
|
||||
std::move(node_services_interface),
|
||||
std::move(node_logging_interface),
|
||||
std::move(node_clock_interface),
|
||||
std::move(node_parameters_interface));
|
||||
}
|
||||
|
||||
void TimeSource::detachNode()
|
||||
{
|
||||
this->ros_time_active_ = false;
|
||||
destroy_clock_sub();
|
||||
parameter_subscription_.reset();
|
||||
node_base_.reset();
|
||||
node_topics_.reset();
|
||||
node_graph_.reset();
|
||||
node_services_.reset();
|
||||
node_logging_.reset();
|
||||
node_clock_.reset();
|
||||
if (sim_time_cb_handler_ && node_parameters_) {
|
||||
node_parameters_->remove_on_set_parameters_callback(sim_time_cb_handler_.get());
|
||||
}
|
||||
sim_time_cb_handler_.reset();
|
||||
node_parameters_.reset();
|
||||
disable_ros_time();
|
||||
node_state_.reset();
|
||||
node_state_ = std::make_shared<NodeState>(
|
||||
clocks_state_->weak_from_this(),
|
||||
constructed_qos_,
|
||||
constructed_use_clock_thread_);
|
||||
}
|
||||
|
||||
void TimeSource::attachClock(std::shared_ptr<rclcpp::Clock> clock)
|
||||
{
|
||||
if (clock->get_clock_type() != RCL_ROS_TIME) {
|
||||
throw std::invalid_argument("Cannot attach clock to a time source that's not a ROS clock");
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> guard(clock_list_lock_);
|
||||
associated_clocks_.push_back(clock);
|
||||
// Set the clock to zero unless there's a recently received message
|
||||
auto time_msg = std::make_shared<builtin_interfaces::msg::Time>();
|
||||
if (last_msg_set_) {
|
||||
time_msg = std::make_shared<builtin_interfaces::msg::Time>(last_msg_set_->clock);
|
||||
}
|
||||
set_clock(time_msg, ros_time_active_, clock);
|
||||
clocks_state_->attachClock(std::move(clock));
|
||||
}
|
||||
|
||||
void TimeSource::detachClock(std::shared_ptr<rclcpp::Clock> clock)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(clock_list_lock_);
|
||||
auto result = std::find(associated_clocks_.begin(), associated_clocks_.end(), clock);
|
||||
if (result != associated_clocks_.end()) {
|
||||
associated_clocks_.erase(result);
|
||||
} else {
|
||||
RCLCPP_ERROR(logger_, "failed to remove clock");
|
||||
}
|
||||
clocks_state_->detachClock(std::move(clock));
|
||||
}
|
||||
|
||||
bool TimeSource::get_use_clock_thread()
|
||||
{
|
||||
return node_state_->get_use_clock_thread();
|
||||
}
|
||||
|
||||
void TimeSource::set_use_clock_thread(bool use_clock_thread)
|
||||
{
|
||||
node_state_->set_use_clock_thread(use_clock_thread);
|
||||
}
|
||||
|
||||
bool TimeSource::clock_thread_is_joinable()
|
||||
{
|
||||
return node_state_->clock_thread_is_joinable();
|
||||
}
|
||||
|
||||
TimeSource::~TimeSource()
|
||||
{
|
||||
if (
|
||||
node_base_ || node_topics_ || node_graph_ || node_services_ ||
|
||||
node_logging_ || node_clock_ || node_parameters_)
|
||||
{
|
||||
this->detachNode();
|
||||
}
|
||||
}
|
||||
|
||||
void TimeSource::set_clock(
|
||||
const builtin_interfaces::msg::Time::SharedPtr msg, bool set_ros_time_enabled,
|
||||
std::shared_ptr<rclcpp::Clock> clock)
|
||||
{
|
||||
std::lock_guard<std::mutex> clock_guard(clock->get_clock_mutex());
|
||||
|
||||
// Do change
|
||||
if (!set_ros_time_enabled && clock->ros_time_is_active()) {
|
||||
auto ret = rcl_disable_ros_time_override(clock->get_clock_handle());
|
||||
if (ret != RCL_RET_OK) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(
|
||||
ret, "Failed to disable ros_time_override_status");
|
||||
}
|
||||
} else if (set_ros_time_enabled && !clock->ros_time_is_active()) {
|
||||
auto ret = rcl_enable_ros_time_override(clock->get_clock_handle());
|
||||
if (ret != RCL_RET_OK) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(
|
||||
ret, "Failed to enable ros_time_override_status");
|
||||
}
|
||||
}
|
||||
|
||||
auto ret = rcl_set_ros_time_override(clock->get_clock_handle(), rclcpp::Time(*msg).nanoseconds());
|
||||
if (ret != RCL_RET_OK) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(
|
||||
ret, "Failed to set ros_time_override_status");
|
||||
}
|
||||
}
|
||||
|
||||
void TimeSource::clock_cb(std::shared_ptr<const rosgraph_msgs::msg::Clock> msg)
|
||||
{
|
||||
if (!this->ros_time_active_ && SET_TRUE == this->parameter_state_) {
|
||||
enable_ros_time();
|
||||
}
|
||||
// Cache the last message in case a new clock is attached.
|
||||
last_msg_set_ = msg;
|
||||
auto time_msg = std::make_shared<builtin_interfaces::msg::Time>(msg->clock);
|
||||
|
||||
if (SET_TRUE == this->parameter_state_) {
|
||||
std::lock_guard<std::mutex> guard(clock_list_lock_);
|
||||
for (auto it = associated_clocks_.begin(); it != associated_clocks_.end(); ++it) {
|
||||
set_clock(time_msg, true, *it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TimeSource::create_clock_sub()
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(clock_sub_lock_);
|
||||
if (clock_subscription_) {
|
||||
// Subscription already created.
|
||||
return;
|
||||
}
|
||||
|
||||
rclcpp::SubscriptionOptions options;
|
||||
options.qos_overriding_options = rclcpp::QosOverridingOptions(
|
||||
{
|
||||
rclcpp::QosPolicyKind::Depth,
|
||||
rclcpp::QosPolicyKind::Durability,
|
||||
rclcpp::QosPolicyKind::History,
|
||||
rclcpp::QosPolicyKind::Reliability,
|
||||
});
|
||||
|
||||
if (use_clock_thread_) {
|
||||
clock_callback_group_ = node_base_->create_callback_group(
|
||||
rclcpp::CallbackGroupType::MutuallyExclusive,
|
||||
false
|
||||
);
|
||||
options.callback_group = clock_callback_group_;
|
||||
rclcpp::ExecutorOptions exec_options;
|
||||
exec_options.context = node_base_->get_context();
|
||||
clock_executor_ =
|
||||
std::make_shared<rclcpp::executors::SingleThreadedExecutor>(exec_options);
|
||||
if (!clock_executor_thread_.joinable()) {
|
||||
cancel_clock_executor_promise_ = std::promise<void>{};
|
||||
clock_executor_thread_ = std::thread(
|
||||
[this]() {
|
||||
auto future = cancel_clock_executor_promise_.get_future();
|
||||
clock_executor_->add_callback_group(clock_callback_group_, node_base_);
|
||||
clock_executor_->spin_until_future_complete(future);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
clock_subscription_ = rclcpp::create_subscription<rosgraph_msgs::msg::Clock>(
|
||||
node_parameters_,
|
||||
node_topics_,
|
||||
"/clock",
|
||||
rclcpp::QoS(KeepLast(1)).best_effort(),
|
||||
std::bind(&TimeSource::clock_cb, this, std::placeholders::_1),
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
void TimeSource::destroy_clock_sub()
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(clock_sub_lock_);
|
||||
if (clock_executor_thread_.joinable()) {
|
||||
cancel_clock_executor_promise_.set_value();
|
||||
clock_executor_->cancel();
|
||||
clock_executor_thread_.join();
|
||||
clock_executor_->remove_callback_group(clock_callback_group_);
|
||||
}
|
||||
clock_subscription_.reset();
|
||||
}
|
||||
|
||||
void TimeSource::on_parameter_event(
|
||||
std::shared_ptr<const rcl_interfaces::msg::ParameterEvent> event)
|
||||
{
|
||||
// Filter out events on 'use_sim_time' parameter instances in other nodes.
|
||||
if (event->node != node_base_->get_fully_qualified_name()) {
|
||||
return;
|
||||
}
|
||||
// Filter for only 'use_sim_time' being added or changed.
|
||||
rclcpp::ParameterEventsFilter filter(event, {"use_sim_time"},
|
||||
{rclcpp::ParameterEventsFilter::EventType::NEW,
|
||||
rclcpp::ParameterEventsFilter::EventType::CHANGED});
|
||||
for (auto & it : filter.get_events()) {
|
||||
if (it.second->value.type != ParameterType::PARAMETER_BOOL) {
|
||||
RCLCPP_ERROR(logger_, "use_sim_time parameter cannot be set to anything but a bool");
|
||||
continue;
|
||||
}
|
||||
if (it.second->value.bool_value) {
|
||||
parameter_state_ = SET_TRUE;
|
||||
enable_ros_time();
|
||||
create_clock_sub();
|
||||
} else {
|
||||
parameter_state_ = SET_FALSE;
|
||||
disable_ros_time();
|
||||
destroy_clock_sub();
|
||||
}
|
||||
}
|
||||
// Handle the case that use_sim_time was deleted.
|
||||
rclcpp::ParameterEventsFilter deleted(event, {"use_sim_time"},
|
||||
{rclcpp::ParameterEventsFilter::EventType::DELETED});
|
||||
for (auto & it : deleted.get_events()) {
|
||||
(void) it; // if there is a match it's already matched, don't bother reading it.
|
||||
// If the parameter is deleted mark it as unset but dont' change state.
|
||||
parameter_state_ = UNSET;
|
||||
}
|
||||
}
|
||||
|
||||
void TimeSource::enable_ros_time()
|
||||
{
|
||||
if (ros_time_active_) {
|
||||
// already enabled no-op
|
||||
return;
|
||||
}
|
||||
|
||||
// Local storage
|
||||
ros_time_active_ = true;
|
||||
|
||||
// Update all attached clocks to zero or last recorded time
|
||||
std::lock_guard<std::mutex> guard(clock_list_lock_);
|
||||
auto time_msg = std::make_shared<builtin_interfaces::msg::Time>();
|
||||
if (last_msg_set_) {
|
||||
time_msg = std::make_shared<builtin_interfaces::msg::Time>(last_msg_set_->clock);
|
||||
}
|
||||
for (auto it = associated_clocks_.begin(); it != associated_clocks_.end(); ++it) {
|
||||
set_clock(time_msg, true, *it);
|
||||
}
|
||||
}
|
||||
|
||||
void TimeSource::disable_ros_time()
|
||||
{
|
||||
if (!ros_time_active_) {
|
||||
// already disabled no-op
|
||||
return;
|
||||
}
|
||||
|
||||
// Local storage
|
||||
ros_time_active_ = false;
|
||||
|
||||
// Update all attached clocks
|
||||
std::lock_guard<std::mutex> guard(clock_list_lock_);
|
||||
for (auto it = associated_clocks_.begin(); it != associated_clocks_.end(); ++it) {
|
||||
auto msg = std::make_shared<builtin_interfaces::msg::Time>();
|
||||
set_clock(msg, false, *it);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
@@ -31,18 +31,22 @@ namespace rclcpp
|
||||
{
|
||||
|
||||
void
|
||||
init(int argc, char const * const argv[], const InitOptions & init_options)
|
||||
init(
|
||||
int argc,
|
||||
char const * const argv[],
|
||||
const InitOptions & init_options,
|
||||
SignalHandlerOptions signal_handler_options)
|
||||
{
|
||||
using rclcpp::contexts::get_global_default_context;
|
||||
get_global_default_context()->init(argc, argv, init_options);
|
||||
// Install the signal handlers.
|
||||
install_signal_handlers();
|
||||
install_signal_handlers(signal_handler_options);
|
||||
}
|
||||
|
||||
bool
|
||||
install_signal_handlers()
|
||||
install_signal_handlers(SignalHandlerOptions signal_handler_options)
|
||||
{
|
||||
return SignalHandler::get_global_signal_handler().install();
|
||||
return SignalHandler::get_global_signal_handler().install(signal_handler_options);
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -51,6 +55,13 @@ signal_handlers_installed()
|
||||
return SignalHandler::get_global_signal_handler().is_installed();
|
||||
}
|
||||
|
||||
SignalHandlerOptions
|
||||
get_current_signal_handler_options()
|
||||
{
|
||||
return SignalHandler::get_global_signal_handler().get_current_signal_handler_options();
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
uninstall_signal_handlers()
|
||||
{
|
||||
@@ -156,12 +167,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)
|
||||
{
|
||||
|
||||
@@ -62,6 +62,7 @@ BENCHMARK_F(ClientPerformanceTest, construct_client_no_service)(benchmark::State
|
||||
|
||||
reset_heap_counters();
|
||||
for (auto _ : state) {
|
||||
(void)_;
|
||||
auto client = node->create_client<test_msgs::srv::Empty>("not_an_existing_service");
|
||||
benchmark::DoNotOptimize(client);
|
||||
benchmark::ClobberMemory();
|
||||
@@ -79,6 +80,7 @@ BENCHMARK_F(ClientPerformanceTest, construct_client_empty_srv)(benchmark::State
|
||||
|
||||
reset_heap_counters();
|
||||
for (auto _ : state) {
|
||||
(void)_;
|
||||
auto client = node->create_client<test_msgs::srv::Empty>(empty_service_name);
|
||||
benchmark::DoNotOptimize(client);
|
||||
benchmark::ClobberMemory();
|
||||
@@ -96,6 +98,7 @@ BENCHMARK_F(ClientPerformanceTest, destroy_client_empty_srv)(benchmark::State &
|
||||
|
||||
reset_heap_counters();
|
||||
for (auto _ : state) {
|
||||
(void)_;
|
||||
state.PauseTiming();
|
||||
auto client = node->create_client<test_msgs::srv::Empty>(empty_service_name);
|
||||
state.ResumeTiming();
|
||||
@@ -109,6 +112,7 @@ BENCHMARK_F(ClientPerformanceTest, destroy_client_empty_srv)(benchmark::State &
|
||||
BENCHMARK_F(ClientPerformanceTest, wait_for_service)(benchmark::State & state) {
|
||||
int count = 0;
|
||||
for (auto _ : state) {
|
||||
(void)_;
|
||||
state.PauseTiming();
|
||||
const std::string service_name = std::string("service_") + std::to_string(count++);
|
||||
// Create client before service so it has to 'discover' the service after construction
|
||||
@@ -132,6 +136,7 @@ BENCHMARK_F(ClientPerformanceTest, async_send_request_only)(benchmark::State & s
|
||||
|
||||
reset_heap_counters();
|
||||
for (auto _ : state) {
|
||||
(void)_;
|
||||
auto future = client->async_send_request(shared_request);
|
||||
benchmark::DoNotOptimize(future);
|
||||
benchmark::ClobberMemory();
|
||||
@@ -144,6 +149,7 @@ BENCHMARK_F(ClientPerformanceTest, async_send_request_and_response)(benchmark::S
|
||||
|
||||
reset_heap_counters();
|
||||
for (auto _ : state) {
|
||||
(void)_;
|
||||
auto future = client->async_send_request(shared_request);
|
||||
rclcpp::spin_until_future_complete(
|
||||
node->get_node_base_interface(), future, std::chrono::seconds(1));
|
||||
|
||||
@@ -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;
|
||||
@@ -78,6 +78,7 @@ BENCHMARK_F(PerformanceTestExecutor, single_thread_executor_spin_some)(benchmark
|
||||
reset_heap_counters();
|
||||
|
||||
for (auto _ : st) {
|
||||
(void)_;
|
||||
st.PauseTiming();
|
||||
for (unsigned int i = 0u; i < kNumberOfNodes; i++) {
|
||||
publishers[i]->publish(empty_msgs);
|
||||
@@ -104,6 +105,7 @@ BENCHMARK_F(PerformanceTestExecutor, multi_thread_executor_spin_some)(benchmark:
|
||||
reset_heap_counters();
|
||||
|
||||
for (auto _ : st) {
|
||||
(void)_;
|
||||
st.PauseTiming();
|
||||
for (unsigned int i = 0u; i < kNumberOfNodes; i++) {
|
||||
publishers[i]->publish(empty_msgs);
|
||||
@@ -142,6 +144,7 @@ BENCHMARK_F(PerformanceTestExecutorSimple, single_thread_executor_add_node)(benc
|
||||
{
|
||||
rclcpp::executors::SingleThreadedExecutor executor;
|
||||
for (auto _ : st) {
|
||||
(void)_;
|
||||
executor.add_node(node);
|
||||
st.PauseTiming();
|
||||
executor.remove_node(node);
|
||||
@@ -154,6 +157,7 @@ BENCHMARK_F(
|
||||
{
|
||||
rclcpp::executors::SingleThreadedExecutor executor;
|
||||
for (auto _ : st) {
|
||||
(void)_;
|
||||
st.PauseTiming();
|
||||
executor.add_node(node);
|
||||
st.ResumeTiming();
|
||||
@@ -165,6 +169,7 @@ BENCHMARK_F(PerformanceTestExecutorSimple, multi_thread_executor_add_node)(bench
|
||||
{
|
||||
rclcpp::executors::MultiThreadedExecutor executor;
|
||||
for (auto _ : st) {
|
||||
(void)_;
|
||||
executor.add_node(node);
|
||||
st.PauseTiming();
|
||||
executor.remove_node(node);
|
||||
@@ -176,6 +181,7 @@ BENCHMARK_F(PerformanceTestExecutorSimple, multi_thread_executor_remove_node)(be
|
||||
{
|
||||
rclcpp::executors::MultiThreadedExecutor executor;
|
||||
for (auto _ : st) {
|
||||
(void)_;
|
||||
st.PauseTiming();
|
||||
executor.add_node(node);
|
||||
st.ResumeTiming();
|
||||
@@ -189,6 +195,7 @@ BENCHMARK_F(
|
||||
{
|
||||
rclcpp::executors::StaticSingleThreadedExecutor executor;
|
||||
for (auto _ : st) {
|
||||
(void)_;
|
||||
executor.add_node(node);
|
||||
st.PauseTiming();
|
||||
executor.remove_node(node);
|
||||
@@ -202,6 +209,7 @@ BENCHMARK_F(
|
||||
{
|
||||
rclcpp::executors::StaticSingleThreadedExecutor executor;
|
||||
for (auto _ : st) {
|
||||
(void)_;
|
||||
st.PauseTiming();
|
||||
executor.add_node(node);
|
||||
st.ResumeTiming();
|
||||
@@ -228,6 +236,7 @@ BENCHMARK_F(
|
||||
reset_heap_counters();
|
||||
|
||||
for (auto _ : st) {
|
||||
(void)_;
|
||||
// static_single_thread_executor has a special design. We need to add/remove the node each
|
||||
// time you call spin
|
||||
st.PauseTiming();
|
||||
@@ -265,6 +274,7 @@ BENCHMARK_F(
|
||||
reset_heap_counters();
|
||||
|
||||
for (auto _ : st) {
|
||||
(void)_;
|
||||
ret = rclcpp::executors::spin_node_until_future_complete(
|
||||
executor, node, shared_future, 1s);
|
||||
if (ret != rclcpp::FutureReturnCode::SUCCESS) {
|
||||
@@ -294,6 +304,7 @@ BENCHMARK_F(
|
||||
reset_heap_counters();
|
||||
|
||||
for (auto _ : st) {
|
||||
(void)_;
|
||||
ret = rclcpp::executors::spin_node_until_future_complete(
|
||||
executor, node, shared_future, 1s);
|
||||
if (ret != rclcpp::FutureReturnCode::SUCCESS) {
|
||||
@@ -317,6 +328,7 @@ BENCHMARK_F(
|
||||
reset_heap_counters();
|
||||
|
||||
for (auto _ : st) {
|
||||
(void)_;
|
||||
auto ret = rclcpp::executors::spin_node_until_future_complete(
|
||||
executor, node, shared_future, 1s);
|
||||
if (ret != rclcpp::FutureReturnCode::SUCCESS) {
|
||||
@@ -342,6 +354,7 @@ BENCHMARK_F(PerformanceTestExecutorSimple, spin_until_future_complete)(benchmark
|
||||
reset_heap_counters();
|
||||
|
||||
for (auto _ : st) {
|
||||
(void)_;
|
||||
ret = rclcpp::spin_until_future_complete(node, shared_future, 1s);
|
||||
if (ret != rclcpp::FutureReturnCode::SUCCESS) {
|
||||
st.SkipWithError(rcutils_get_error_string().str);
|
||||
@@ -366,7 +379,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,14 +389,14 @@ 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();
|
||||
|
||||
for (auto _ : st) {
|
||||
(void)_;
|
||||
std::shared_ptr<void> data = entities_collector_->take_data();
|
||||
entities_collector_->execute(data);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user