Compare commits

...

17 Commits

Author SHA1 Message Date
Chris Lalancette
f57f4077fd 19.2.0 2023-02-24 18:27:42 +00:00
Chris Lalancette
006d1fa1df Changelog.
Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2023-02-24 18:27:32 +00:00
Chen Lihui
b1e834a8df to create a sublogger while getting child of Logger (#1717)
* to create a sublogger while getting child of Logger

Signed-off-by: Chen Lihui <lihui.chen@sony.com>
2023-02-24 11:03:10 -05:00
Silvio Traversaro
28e4b1bd73 Fix documentation of Context class (#2107)
Signed-off-by: Silvio Traversaro <silvio@traversaro.it>
2023-02-16 16:32:10 -05:00
Alberto Soragna
35a5d6a66c fixes for rmw callbacks in qos_event class (#2102)
Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>
2023-02-16 07:56:47 -05:00
Chris Lalancette
01b19247f1 19.1.0 2023-02-14 14:36:12 +00:00
Chris Lalancette
15ea024d48 Changelog.
Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2023-02-14 14:35:58 +00:00
mauropasse
ce5a2614fa Add support for timers on reset callback (#1979)
Signed-off-by: Mauro Passerino <mpasserino@irobot.com>
Co-authored-by: Alberto Soragna <alberto.soragna@gmail.com>
2023-02-10 08:25:52 -08:00
Chen Lihui
beda0966db Topic node guard condition in executor (#2074)
* add a test

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

* rollback the node guard condition for exectuor

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

---------

Signed-off-by: Chen Lihui <lihui.chen@sony.com>
2023-02-10 11:39:27 +00:00
Barry Xu
1fd5a96561 Fix bug on the disorder of calling shutdown callback (#2097)
* Fix bug on the disorder of calling shutdown callback

Signed-off-by: Barry Xu <barry.xu@sony.com>
2023-02-06 12:46:47 -05:00
Shane Loretz
be8c5d01c6 19.0.0
Signed-off-by: Shane Loretz <sloretz@openrobotics.org>
2023-01-30 18:26:56 -08:00
Shane Loretz
97c5c11c25 Add default constructor to NodeInterfaces (#2094)
* Add default constructor to NodeInterfaces

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

* Remove unnecessary NOLINT

Signed-off-by: Shane Loretz <sloretz@google.com>

---------

Signed-off-by: Shane Loretz <sloretz@openrobotics.org>
Signed-off-by: Shane Loretz <sloretz@google.com>
2023-01-30 13:47:11 -08:00
Chris Lalancette
7d660acc05 Fix clock state cached time to be a copy, not a reference. (#2092)
That is, in cache_last_msg(), we were just taking a shared_ptr
reference.  While this is pretty fast, it also means that
any changes to that message would be reflected internally.
Instead, make a new shared pointer out of that message when
it comes in, which essentially causes this to be a copy.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2023-01-27 14:16:31 -05:00
Michael Carroll
ab71df3ce1 Improve component_manager_isolated shutdown (#2085)
This eliminates a potential hang when the isolated container is being
shutdown via the rclcpp SignalHandler.

Signed-off-by: Michael Carroll <michael@openrobotics.org>
Co-authored-by: Chris Lalancette <clalancette@openrobotics.org>
2023-01-23 09:46:50 -06:00
Alexander Hans
37adc03c11 Fix -Wmaybe-uninitialized warning (#2081)
* Fix -Wmaybe-uninitialized warning

gcc 12 warned about `callback_list_ptr` potentially being uninitialized
when compiling in release mode (i.e., with `-O2`). Since `shutdown_type`
is a compile-time parameter, we fix the warning by enforcing the
decision at compile time.

Signed-off-by: Alexander Hans <ahans@users.noreply.github.com>
2023-01-17 14:50:44 -05:00
Chris Lalancette
3db2ece145 Fix the keep_last warning when using system defaults. (#2082)
If the user creates SystemDefaultsQoS setting, they are
explicitly asking for SystemDefaults.  In that case, we should
*not* warn, and just let the underlying RMW choose what it
wants.  Implement that here by passing a boolean parameter
through that decides when we should print out the warning,
and then skip printing that warning when SystemDefaultsQoS
is created.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2023-01-13 13:47:13 -08:00
Chris Lalancette
1bbb03302a Add in a fix for older compilers. (#2075)
* Add in a fix for older compilers.

The addition of the NodeInterfaces class made it stop compiling
with older compilers (such as gcc 9.4.0 on Ubuntu 20.04).
The error has to do with calling the copy constructor on
rclcpp::Node, which is deleted.  Work around this by removing
the NodeInterfaces shared_ptr constructor, which isn't technically
needed.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2023-01-04 08:49:57 -05:00
29 changed files with 860 additions and 149 deletions

View File

@@ -2,6 +2,29 @@
Changelog for package rclcpp
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
19.2.0 (2023-02-24)
-------------------
* to create a sublogger while getting child of Logger (`#1717 <https://github.com/ros2/rclcpp/issues/1717>`_)
* Fix documentation of Context class (`#2107 <https://github.com/ros2/rclcpp/issues/2107>`_)
* fixes for rmw callbacks in qos_event class (`#2102 <https://github.com/ros2/rclcpp/issues/2102>`_)
* Contributors: Alberto Soragna, Chen Lihui, Silvio Traversaro
19.1.0 (2023-02-14)
-------------------
* Add support for timers on reset callback (`#1979 <https://github.com/ros2/rclcpp/issues/1979>`_)
* Topic node guard condition in executor (`#2074 <https://github.com/ros2/rclcpp/issues/2074>`_)
* Fix bug on the disorder of calling shutdown callback (`#2097 <https://github.com/ros2/rclcpp/issues/2097>`_)
* Contributors: Barry Xu, Chen Lihui, mauropasse
19.0.0 (2023-01-30)
-------------------
* Add default constructor to NodeInterfaces (`#2094 <https://github.com/ros2/rclcpp/issues/2094>`_)
* Fix clock state cached time to be a copy, not a reference. (`#2092 <https://github.com/ros2/rclcpp/issues/2092>`_)
* Fix -Wmaybe-uninitialized warning (`#2081 <https://github.com/ros2/rclcpp/issues/2081>`_)
* Fix the keep_last warning when using system defaults. (`#2082 <https://github.com/ros2/rclcpp/issues/2082>`_)
* Add in a fix for older compilers. (`#2075 <https://github.com/ros2/rclcpp/issues/2075>`_)
* Contributors: Alexander Hans, Chris Lalancette, Shane Loretz
18.0.0 (2022-12-29)
-------------------
* Implement Unified Node Interface (NodeInterfaces class) (`#2041 <https://github.com/ros2/rclcpp/issues/2041>`_)

View File

@@ -65,8 +65,11 @@ 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.
* It is often used in conjunction with rclcpp::init, or rclcpp::init_local,
* and rclcpp::shutdown.
* Nodes may be attached to a particular context by passing to the rclcpp::Node
* constructor a rclcpp::NodeOptions instance in which the Context is set via
* rclcpp::NodeOptions::context.
* Nodes will be automatically removed from the context when destructed.
* Contexts may be shutdown by calling rclcpp::shutdown.
*/
class Context : public std::enable_shared_from_this<Context>
{
@@ -376,10 +379,10 @@ private:
// attempt to acquire another sub context.
std::recursive_mutex sub_contexts_mutex_;
std::unordered_set<std::shared_ptr<OnShutdownCallback>> on_shutdown_callbacks_;
std::vector<std::shared_ptr<OnShutdownCallback>> on_shutdown_callbacks_;
mutable std::mutex on_shutdown_callbacks_mutex_;
std::unordered_set<std::shared_ptr<PreShutdownCallback>> pre_shutdown_callbacks_;
std::vector<std::shared_ptr<PreShutdownCallback>> pre_shutdown_callbacks_;
mutable std::mutex pre_shutdown_callbacks_mutex_;
/// Condition variable for timed sleep (see sleep_for).
@@ -398,20 +401,22 @@ private:
using ShutdownCallback = ShutdownCallbackHandle::ShutdownCallbackType;
template<ShutdownType shutdown_type>
RCLCPP_LOCAL
ShutdownCallbackHandle
add_shutdown_callback(
ShutdownType shutdown_type,
ShutdownCallback callback);
template<ShutdownType shutdown_type>
RCLCPP_LOCAL
bool
remove_shutdown_callback(
ShutdownType shutdown_type,
const ShutdownCallbackHandle & callback_handle);
template<ShutdownType shutdown_type>
RCLCPP_LOCAL
std::vector<rclcpp::Context::ShutdownCallback>
get_shutdown_callback(ShutdownType shutdown_type) const;
get_shutdown_callback() const;
};
/// Return a copy of the list of context shared pointers.

View File

@@ -560,11 +560,20 @@ protected:
virtual void
spin_once_impl(std::chrono::nanoseconds timeout);
typedef std::map<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr,
const rclcpp::GuardCondition *,
std::owner_less<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>>
WeakNodesToGuardConditionsMap;
typedef std::map<rclcpp::CallbackGroup::WeakPtr,
const rclcpp::GuardCondition *,
std::owner_less<rclcpp::CallbackGroup::WeakPtr>>
WeakCallbackGroupsToGuardConditionsMap;
/// maps nodes to guard conditions
WeakNodesToGuardConditionsMap
weak_nodes_to_guard_conditions_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// maps callback groups to guard conditions
WeakCallbackGroupsToGuardConditionsMap
weak_groups_to_guard_conditions_ RCPPUTILS_TSA_GUARDED_BY(mutex_);

View File

@@ -17,6 +17,7 @@
#include <memory>
#include <string>
#include <utility>
#include "rclcpp/visibility_control.hpp"
@@ -122,6 +123,7 @@ private:
: name_(new std::string(name)) {}
std::shared_ptr<const std::string> name_;
std::shared_ptr<std::pair<std::string, std::string>> logger_sublogger_pairname_ = nullptr;
public:
RCLCPP_PUBLIC
@@ -157,13 +159,7 @@ public:
*/
RCLCPP_PUBLIC
Logger
get_child(const std::string & suffix)
{
if (!name_) {
return Logger();
}
return Logger(*name_ + "." + suffix);
}
get_child(const std::string & suffix);
/// Set level for current logger.
/**

View File

@@ -44,6 +44,10 @@ struct NodeInterfacesStorage
: interfaces_(init_tuple<decltype(node), InterfaceTs ...>(node))
{}
NodeInterfacesStorage()
: interfaces_()
{}
explicit NodeInterfacesStorage(std::shared_ptr<InterfaceTs>... args)
: interfaces_(args ...)
{}

View File

@@ -147,10 +147,9 @@ public:
: NodeInterfacesSupportsT(node)
{}
/// NodeT::SharedPtr Constructor
template<typename NodeT>
NodeInterfaces(std::shared_ptr<NodeT> node) // NOLINT(runtime/explicit)
: NodeInterfaces(node ? *node : throw std::invalid_argument("given node pointer is nullptr"))
// Create a NodeInterfaces object with no bound interfaces
NodeInterfaces()
: NodeInterfacesSupportsT()
{}
explicit NodeInterfaces(std::shared_ptr<InterfaceTs>... args)

View File

@@ -80,7 +80,9 @@ struct RCLCPP_PUBLIC QoSInitialization
size_t depth;
/// Constructor which takes both a history policy and a depth (even if it would be unused).
QoSInitialization(rmw_qos_history_policy_t history_policy_arg, size_t depth_arg);
QoSInitialization(
rmw_qos_history_policy_t history_policy_arg, size_t depth_arg,
bool print_depth_warning = true);
/// Create a QoSInitialization from an existing rmw_qos_profile_t, using its history and depth.
static
@@ -97,7 +99,7 @@ struct RCLCPP_PUBLIC KeepAll : public rclcpp::QoSInitialization
/// Use to initialize the QoS with the keep_last history setting and the given depth.
struct RCLCPP_PUBLIC KeepLast : public rclcpp::QoSInitialization
{
explicit KeepLast(size_t depth);
explicit KeepLast(size_t depth, bool print_depth_warning = true);
};
/// Encapsulation of Quality of Service settings.

View File

@@ -214,10 +214,11 @@ protected:
void
set_on_new_event_callback(rcl_event_callback_t callback, const void * user_data);
rcl_event_t event_handle_;
size_t wait_set_event_index_;
std::recursive_mutex callback_mutex_;
std::function<void(size_t)> on_new_event_callback_{nullptr};
rcl_event_t event_handle_;
size_t wait_set_event_index_;
};
template<typename EventCallbackT, typename ParentHandleT>

View File

@@ -149,11 +149,48 @@ public:
bool
exchange_in_use_by_wait_set_state(bool in_use_state);
/// Set a callback to be called when the timer is reset
/**
* You should aim to make this callback fast and not blocking.
* If you need to do a lot of work or wait for some other event, you should
* spin it off to another thread.
*
* Calling it again will override any previously set callback.
* An exception will be thrown if the callback is not callable.
*
* This function is thread-safe.
*
* If you want more information available in the callback,
* you may use a lambda with captures or std::bind.
*
* \param[in] callback functor to be called whenever timer is reset
*/
RCLCPP_PUBLIC
void
set_on_reset_callback(std::function<void(size_t)> callback);
/// Unset the callback registered for reset timer
RCLCPP_PUBLIC
void
clear_on_reset_callback();
protected:
std::recursive_mutex callback_mutex_;
// Declare callback before timer_handle_, so on destruction
// the callback is destroyed last. Otherwise, the rcl timer
// callback would point briefly to a destroyed function.
// Clearing the callback on timer destructor also makes sure
// the rcl callback is cleared before on_reset_callback_.
std::function<void(size_t)> on_reset_callback_{nullptr};
Clock::SharedPtr clock_;
std::shared_ptr<rcl_timer_t> timer_handle_;
std::atomic<bool> in_use_by_wait_set_{false};
RCLCPP_PUBLIC
void
set_on_reset_callback(rcl_event_callback_t callback, const void * user_data);
};

View File

@@ -2,7 +2,7 @@
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="2">
<name>rclcpp</name>
<version>18.0.0</version>
<version>19.2.0</version>
<description>The ROS client library in C++.</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>

View File

@@ -365,49 +365,45 @@ Context::on_shutdown(OnShutdownCallback callback)
rclcpp::OnShutdownCallbackHandle
Context::add_on_shutdown_callback(OnShutdownCallback callback)
{
return add_shutdown_callback(ShutdownType::on_shutdown, callback);
return add_shutdown_callback<ShutdownType::on_shutdown>(callback);
}
bool
Context::remove_on_shutdown_callback(const OnShutdownCallbackHandle & callback_handle)
{
return remove_shutdown_callback(ShutdownType::on_shutdown, callback_handle);
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);
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);
return remove_shutdown_callback<ShutdownType::pre_shutdown>(callback_handle);
}
template<Context::ShutdownType shutdown_type>
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;
static_assert(
shutdown_type == ShutdownType::pre_shutdown || shutdown_type == ShutdownType::on_shutdown);
if constexpr (shutdown_type == ShutdownType::pre_shutdown) {
std::lock_guard<std::mutex> lock(pre_shutdown_callbacks_mutex_);
pre_shutdown_callbacks_.emplace_back(callback_shared_ptr);
} else {
std::lock_guard<std::mutex> lock(on_shutdown_callbacks_mutex_);
on_shutdown_callbacks_.emplace_back(callback_shared_ptr);
}
ShutdownCallbackHandle callback_handle;
@@ -415,73 +411,74 @@ Context::add_shutdown_callback(
return callback_handle;
}
template<Context::ShutdownType shutdown_type>
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();
const auto callback_shared_ptr = callback_handle.callback.lock();
if (callback_shared_ptr == nullptr) {
return false;
}
return callback_list_ptr->erase(callback_shared_ptr) == 1;
const auto remove_callback = [&callback_shared_ptr](auto & mutex, auto & callback_vector) {
const std::lock_guard<std::mutex> lock(mutex);
auto iter = callback_vector.begin();
for (; iter != callback_vector.end(); iter++) {
if ((*iter).get() == callback_shared_ptr.get()) {
break;
}
}
if (iter == callback_vector.end()) {
return false;
}
callback_vector.erase(iter);
return true;
};
static_assert(
shutdown_type == ShutdownType::pre_shutdown || shutdown_type == ShutdownType::on_shutdown);
if constexpr (shutdown_type == ShutdownType::pre_shutdown) {
return remove_callback(pre_shutdown_callbacks_mutex_, pre_shutdown_callbacks_);
} else {
return remove_callback(on_shutdown_callbacks_mutex_, on_shutdown_callbacks_);
}
}
std::vector<rclcpp::Context::OnShutdownCallback>
Context::get_on_shutdown_callbacks() const
{
return get_shutdown_callback(ShutdownType::on_shutdown);
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);
return get_shutdown_callback<ShutdownType::pre_shutdown>();
}
template<Context::ShutdownType shutdown_type>
std::vector<rclcpp::Context::ShutdownCallback>
Context::get_shutdown_callback(ShutdownType shutdown_type) const
Context::get_shutdown_callback() const
{
std::mutex * mutex_ptr = nullptr;
const std::unordered_set<
std::shared_ptr<ShutdownCallbackHandle::ShutdownCallbackType>> * callback_list_ptr;
const auto get_callback_vector = [this](auto & mutex, auto & callback_set) {
const std::lock_guard<std::mutex> lock(mutex);
std::vector<rclcpp::Context::ShutdownCallback> callbacks;
for (auto & callback : callback_set) {
callbacks.push_back(*callback);
}
return callbacks;
};
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;
static_assert(
shutdown_type == ShutdownType::pre_shutdown || shutdown_type == ShutdownType::on_shutdown);
if constexpr (shutdown_type == ShutdownType::pre_shutdown) {
return get_callback_vector(pre_shutdown_callbacks_mutex_, pre_shutdown_callbacks_);
} else {
return get_callback_vector(on_shutdown_callbacks_mutex_, on_shutdown_callbacks_);
}
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>

View File

@@ -112,6 +112,12 @@ Executor::~Executor()
}
weak_groups_to_guard_conditions_.clear();
for (const auto & pair : weak_nodes_to_guard_conditions_) {
auto guard_condition = pair.second;
memory_strategy_->remove_guard_condition(guard_condition);
}
weak_nodes_to_guard_conditions_.clear();
// Finalize the wait set.
if (rcl_wait_set_fini(&wait_set_) != RCL_RET_OK) {
RCUTILS_LOG_ERROR_NAMED(
@@ -274,6 +280,10 @@ Executor::add_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_pt
}
});
const auto & gc = node_ptr->get_notify_guard_condition();
weak_nodes_to_guard_conditions_[node_ptr] = &gc;
// Add the node's notify condition to the guard condition handles
memory_strategy_->add_guard_condition(gc);
weak_nodes_.push_back(node_ptr);
}
@@ -378,6 +388,9 @@ Executor::remove_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node
}
}
memory_strategy_->remove_guard_condition(&node_ptr->get_notify_guard_condition());
weak_nodes_to_guard_conditions_.erase(node_ptr);
std::atomic_bool & has_executor = node_ptr->get_associated_with_executor_atomic();
has_executor.store(false);
}
@@ -706,6 +719,12 @@ Executor::wait_for_work(std::chrono::nanoseconds timeout)
auto weak_node_ptr = pair.second;
if (weak_group_ptr.expired() || weak_node_ptr.expired()) {
invalid_group_ptrs.push_back(weak_group_ptr);
auto node_guard_pair = weak_nodes_to_guard_conditions_.find(weak_node_ptr);
if (node_guard_pair != weak_nodes_to_guard_conditions_.end()) {
auto guard_condition = node_guard_pair->second;
weak_nodes_to_guard_conditions_.erase(weak_node_ptr);
memory_strategy_->remove_guard_condition(guard_condition);
}
}
}
std::for_each(

View File

@@ -12,14 +12,19 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <memory>
#include <string>
#include <utility>
#include "rcl_logging_interface/rcl_logging_interface.h"
#include "rcl/logging_rosout.h"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/logger.hpp"
#include "rclcpp/logging.hpp"
#include "./logging_mutex.hpp"
namespace rclcpp
{
@@ -62,6 +67,46 @@ get_logging_directory()
return path;
}
Logger
Logger::get_child(const std::string & suffix)
{
if (!name_) {
return Logger();
}
rcl_ret_t rcl_ret = RCL_RET_OK;
std::shared_ptr<std::recursive_mutex> logging_mutex;
logging_mutex = get_global_logging_mutex();
{
std::lock_guard<std::recursive_mutex> guard(*logging_mutex);
rcl_ret = rcl_logging_rosout_add_sublogger((*name_).c_str(), suffix.c_str());
if (RCL_RET_OK != rcl_ret) {
exceptions::throw_from_rcl_error(
rcl_ret, "failed to call rcl_logging_rosout_add_sublogger",
rcutils_get_error_state(), rcutils_reset_error);
}
}
Logger logger(*name_ + RCUTILS_LOGGING_SEPARATOR_STRING + suffix);
if (RCL_RET_OK == rcl_ret) {
logger.logger_sublogger_pairname_.reset(
new std::pair<std::string, std::string>({*name_, suffix}),
[logging_mutex](std::pair<std::string, std::string> * logger_sublogger_pairname_ptr) {
std::lock_guard<std::recursive_mutex> guard(*logging_mutex);
rcl_ret_t rcl_ret = rcl_logging_rosout_remove_sublogger(
logger_sublogger_pairname_ptr->first.c_str(),
logger_sublogger_pairname_ptr->second.c_str());
delete logger_sublogger_pairname_ptr;
if (RCL_RET_OK != rcl_ret) {
exceptions::throw_from_rcl_error(
rcl_ret, "failed to call rcl_logging_rosout_remove_sublogger",
rcutils_get_error_state(), rcutils_reset_error);
}
});
}
return logger;
}
void
Logger::set_level(Level level)
{

View File

@@ -45,9 +45,19 @@ std::string qos_policy_name_from_kind(rmw_qos_policy_kind_t policy_kind)
}
}
QoSInitialization::QoSInitialization(rmw_qos_history_policy_t history_policy_arg, size_t depth_arg)
QoSInitialization::QoSInitialization(
rmw_qos_history_policy_t history_policy_arg, size_t depth_arg,
bool print_depth_warning)
: history_policy(history_policy_arg), depth(depth_arg)
{}
{
if (history_policy == RMW_QOS_POLICY_HISTORY_KEEP_LAST && depth == 0 && print_depth_warning) {
RCLCPP_WARN_ONCE(
rclcpp::get_logger(
"rclcpp"),
"A zero depth with KEEP_LAST doesn't make sense; no data could be stored. "
"This will be interpreted as SYSTEM_DEFAULT");
}
}
QoSInitialization
QoSInitialization::from_rmw(const rmw_qos_profile_t & rmw_qos)
@@ -55,8 +65,9 @@ QoSInitialization::from_rmw(const rmw_qos_profile_t & rmw_qos)
switch (rmw_qos.history) {
case RMW_QOS_POLICY_HISTORY_KEEP_ALL:
return KeepAll();
case RMW_QOS_POLICY_HISTORY_KEEP_LAST:
case RMW_QOS_POLICY_HISTORY_SYSTEM_DEFAULT:
return KeepLast(rmw_qos.depth, false);
case RMW_QOS_POLICY_HISTORY_KEEP_LAST:
case RMW_QOS_POLICY_HISTORY_UNKNOWN:
default:
return KeepLast(rmw_qos.depth);
@@ -67,16 +78,9 @@ KeepAll::KeepAll()
: QoSInitialization(RMW_QOS_POLICY_HISTORY_KEEP_ALL, 0)
{}
KeepLast::KeepLast(size_t depth)
: QoSInitialization(RMW_QOS_POLICY_HISTORY_KEEP_LAST, depth)
KeepLast::KeepLast(size_t depth, bool print_depth_warning)
: QoSInitialization(RMW_QOS_POLICY_HISTORY_KEEP_LAST, depth, print_depth_warning)
{
if (depth == 0) {
RCLCPP_WARN_ONCE(
rclcpp::get_logger(
"rclcpp"),
"A zero depth with KEEP_LAST doesn't make sense; no data could be stored."
"This will be interpreted as SYSTEM_DEFAULT");
}
}
QoS::QoS(
@@ -84,15 +88,6 @@ QoS::QoS(
const rmw_qos_profile_t & initial_profile)
: rmw_qos_profile_(initial_profile)
{
if (qos_initialization.history_policy == RMW_QOS_POLICY_HISTORY_KEEP_LAST &&
qos_initialization.depth == 0)
{
RCLCPP_WARN_ONCE(
rclcpp::get_logger(
"rclcpp"),
"A zero depth with KEEP_LAST doesn't make sense; no data could be stored."
"This will be interpreted as SYSTEM_DEFAULT");
}
rmw_qos_profile_.history = qos_initialization.history_policy;
rmw_qos_profile_.depth = qos_initialization.depth;
}

View File

@@ -40,9 +40,7 @@ QOSEventHandlerBase::~QOSEventHandlerBase()
// This clearing is not needed for other rclcpp entities like pub/subs, since
// they do own the underlying rmw entities, which are destroyed
// on their rclcpp destructors, thus no risk of dangling pointers.
if (on_new_event_callback_) {
clear_on_ready_callback();
}
clear_on_ready_callback();
if (rcl_event_fini(&event_handle_) != RCL_RET_OK) {
RCUTILS_LOG_ERROR_NAMED(

View File

@@ -37,7 +37,8 @@ class ClocksState final
{
public:
ClocksState()
: logger_(rclcpp::get_logger("rclcpp"))
: logger_(rclcpp::get_logger("rclcpp")),
last_time_msg_(std::make_shared<builtin_interfaces::msg::Time>())
{
}
@@ -53,13 +54,8 @@ public:
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);
set_clock(last_time_msg_, true, *it);
}
}
@@ -101,11 +97,7 @@ public:
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);
set_clock(last_time_msg_, ros_time_active_, clock);
}
// Detach a clock
@@ -171,7 +163,7 @@ public:
// Cache the last clock message received
void cache_last_msg(std::shared_ptr<const rosgraph_msgs::msg::Clock> msg)
{
last_msg_set_ = msg;
last_time_msg_ = std::make_shared<builtin_interfaces::msg::Time>(msg->clock);
}
bool are_all_clocks_rcl_ros_time()
@@ -199,7 +191,7 @@ private:
// 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_;
std::shared_ptr<builtin_interfaces::msg::Time> last_time_msg_{nullptr};
};
class TimeSource::NodeState final

View File

@@ -19,9 +19,12 @@
#include <memory>
#include <thread>
#include "rclcpp/contexts/default_context.hpp"
#include "rclcpp/exceptions.hpp"
#include "rmw/impl/cpp/demangle.hpp"
#include "rclcpp/contexts/default_context.hpp"
#include "rclcpp/detail/cpp_callback_trampoline.hpp"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/logging.hpp"
#include "rcutils/logging_macros.h"
using rclcpp::TimerBase;
@@ -71,7 +74,9 @@ TimerBase::TimerBase(
}
TimerBase::~TimerBase()
{}
{
clear_on_reset_callback();
}
void
TimerBase::cancel()
@@ -96,7 +101,11 @@ TimerBase::is_canceled()
void
TimerBase::reset()
{
rcl_ret_t ret = rcl_timer_reset(timer_handle_.get());
rcl_ret_t ret = RCL_RET_OK;
{
std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
ret = rcl_timer_reset(timer_handle_.get());
}
if (ret != RCL_RET_OK) {
rclcpp::exceptions::throw_from_rcl_error(ret, "Couldn't reset timer");
}
@@ -138,3 +147,73 @@ TimerBase::exchange_in_use_by_wait_set_state(bool in_use_state)
{
return in_use_by_wait_set_.exchange(in_use_state);
}
void
TimerBase::set_on_reset_callback(std::function<void(size_t)> callback)
{
if (!callback) {
throw std::invalid_argument(
"The callback passed to set_on_reset_callback "
"is not callable.");
}
auto new_callback =
[callback, this](size_t reset_calls) {
try {
callback(reset_calls);
} catch (const std::exception & exception) {
RCLCPP_ERROR_STREAM(
rclcpp::get_logger("rclcpp"),
"rclcpp::TimerBase@" << this <<
" caught " << rmw::impl::cpp::demangle(exception) <<
" exception in user-provided callback for the 'on reset' callback: " <<
exception.what());
} catch (...) {
RCLCPP_ERROR_STREAM(
rclcpp::get_logger("rclcpp"),
"rclcpp::TimerBase@" << this <<
" caught unhandled exception in user-provided callback " <<
"for the 'on reset' callback");
}
};
std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
// Set it temporarily to the new callback, while we replace the old one.
// This two-step setting, prevents a gap where the old std::function has
// been replaced but rcl hasn't been told about the new one yet.
set_on_reset_callback(
rclcpp::detail::cpp_callback_trampoline<
decltype(new_callback), const void *, size_t>,
static_cast<const void *>(&new_callback));
// Store the std::function to keep it in scope, also overwrites the existing one.
on_reset_callback_ = new_callback;
// Set it again, now using the permanent storage.
set_on_reset_callback(
rclcpp::detail::cpp_callback_trampoline<
decltype(on_reset_callback_), const void *, size_t>,
static_cast<const void *>(&on_reset_callback_));
}
void
TimerBase::clear_on_reset_callback()
{
std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
if (on_reset_callback_) {
set_on_reset_callback(nullptr, nullptr);
on_reset_callback_ = nullptr;
}
}
void
TimerBase::set_on_reset_callback(rcl_event_callback_t callback, const void * user_data)
{
rcl_ret_t ret = rcl_timer_set_on_reset_callback(timer_handle_.get(), callback, user_data);
if (ret != RCL_RET_OK) {
rclcpp::exceptions::throw_from_rcl_error(ret, "Failed to set timer on reset callback");
}
}

View File

@@ -582,6 +582,9 @@ target_link_libraries(test_logger ${PROJECT_NAME})
ament_add_gmock(test_logging test_logging.cpp)
target_link_libraries(test_logging ${PROJECT_NAME})
ament_add_gmock(test_context test_context.cpp)
target_link_libraries(test_context ${PROJECT_NAME})
ament_add_gtest(test_time test_time.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_time)
@@ -736,6 +739,12 @@ if(TARGET test_rosout_qos)
target_link_libraries(test_rosout_qos ${PROJECT_NAME})
endif()
ament_add_gtest(test_rosout_subscription test_rosout_subscription.cpp)
if(TARGET test_rosout_subscription)
ament_target_dependencies(test_rosout_subscription "rcl")
target_link_libraries(test_rosout_subscription ${PROJECT_NAME})
endif()
ament_add_gtest(test_executor test_executor.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}"
TIMEOUT 120)

View File

@@ -33,6 +33,15 @@ protected:
}
};
TEST_F(TestNodeInterfaces, default_constructor) {
auto node = std::make_shared<rclcpp::Node>("my_node");
using rclcpp::node_interfaces::NodeInterfaces;
using rclcpp::node_interfaces::NodeBaseInterface;
using rclcpp::node_interfaces::NodeGraphInterface;
NodeInterfaces<NodeBaseInterface, NodeGraphInterface> interfaces;
interfaces = NodeInterfaces<NodeBaseInterface, NodeGraphInterface>(*node);
}
/*
Testing NodeInterfaces construction from nodes.
*/
@@ -44,7 +53,7 @@ TEST_F(TestNodeInterfaces, node_interfaces_nominal) {
using rclcpp::node_interfaces::NodeInterfaces;
using rclcpp::node_interfaces::NodeBaseInterface;
using rclcpp::node_interfaces::NodeGraphInterface;
auto node_interfaces = NodeInterfaces<NodeBaseInterface, NodeGraphInterface>(node);
auto node_interfaces = NodeInterfaces<NodeBaseInterface, NodeGraphInterface>(*node);
}
// Implicit conversion of rclcpp::Node into function that uses NodeInterfaces of base.
@@ -55,7 +64,7 @@ TEST_F(TestNodeInterfaces, node_interfaces_nominal) {
auto base_interface = ni.get<NodeBaseInterface>();
};
some_func(node);
some_func(*node);
}
// Implicit narrowing of NodeInterfaces into a new interface NodeInterfaces with fewer interfaces.
@@ -67,7 +76,7 @@ TEST_F(TestNodeInterfaces, node_interfaces_nominal) {
auto base_interface = ni_with_one.get<NodeBaseInterface>();
};
NodeInterfaces<NodeBaseInterface, NodeGraphInterface> ni_with_two(node);
NodeInterfaces<NodeBaseInterface, NodeGraphInterface> ni_with_two(*node);
some_func(ni_with_two);
}
@@ -102,7 +111,7 @@ TEST_F(TestNodeInterfaces, node_interfaces_standard_interfaces) {
rclcpp::node_interfaces::NodeWaitablesInterface,
rclcpp::node_interfaces::NodeParametersInterface,
rclcpp::node_interfaces::NodeTimeSourceInterface
>(node);
>(*node);
}
/*
@@ -134,7 +143,7 @@ TEST_F(TestNodeInterfaces, ni_init) {
NodeWaitablesInterface,
NodeParametersInterface,
NodeTimeSourceInterface
>(node);
>(*node);
{
auto base = ni.get<NodeBaseInterface>();
@@ -198,7 +207,7 @@ TEST_F(TestNodeInterfaces, ni_all_init) {
using rclcpp::node_interfaces::NodeParametersInterface;
using rclcpp::node_interfaces::NodeTimeSourceInterface;
auto ni = rclcpp::node_interfaces::NodeInterfaces<ALL_RCLCPP_NODE_INTERFACES>(node);
auto ni = rclcpp::node_interfaces::NodeInterfaces<ALL_RCLCPP_NODE_INTERFACES>(*node);
{
auto base = ni.get<NodeBaseInterface>();

View File

@@ -340,6 +340,53 @@ TYPED_TEST(TestAddCallbackGroupsToExecutor, subscriber_triggered_to_receive_mess
EXPECT_TRUE(received_message_future.get());
}
/*
* Test callback group created after spin.
* A subscriber with a new callback group that created after executor spin not received a message
* because the executor can't be triggered while a subscriber created, see
* https://github.com/ros2/rclcpp/issues/2067
*/
TYPED_TEST(TestAddCallbackGroupsToExecutor, callback_group_create_after_spin)
{
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
// create a publisher to send data
rclcpp::QoS qos = rclcpp::QoS(1).reliable().transient_local();
rclcpp::Publisher<test_msgs::msg::Empty>::SharedPtr publisher =
node->create_publisher<test_msgs::msg::Empty>("topic_name", qos);
publisher->publish(test_msgs::msg::Empty());
// create a thread running an executor
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node);
std::promise<bool> received_message_promise;
auto received_message_future = received_message_promise.get_future();
rclcpp::FutureReturnCode return_code = rclcpp::FutureReturnCode::TIMEOUT;
std::thread executor_thread = std::thread(
[&executor, &received_message_future, &return_code]() {
return_code = executor.spin_until_future_complete(received_message_future, 5s);
});
// to create a callback group after spin
std::this_thread::sleep_for(std::chrono::milliseconds(100));
rclcpp::CallbackGroup::SharedPtr cb_grp = node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive);
// expect the subscriber to receive a message
auto sub_callback = [&received_message_promise](test_msgs::msg::Empty::ConstSharedPtr) {
received_message_promise.set_value(true);
};
// create a subscription using the `cb_grp` callback group
auto options = rclcpp::SubscriptionOptions();
options.callback_group = cb_grp;
rclcpp::Subscription<test_msgs::msg::Empty>::SharedPtr subscription =
node->create_subscription<test_msgs::msg::Empty>("topic_name", qos, sub_callback, options);
executor_thread.join();
ASSERT_EQ(rclcpp::FutureReturnCode::SUCCESS, return_code);
EXPECT_TRUE(received_message_future.get());
}
/*
* Test removing callback group from executor that its not associated with.
*/

View File

@@ -0,0 +1,216 @@
// Copyright 2023 Sony Group Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <gtest/gtest.h>
#include "rclcpp/context.hpp"
#include "rclcpp/rclcpp.hpp"
TEST(TestContext, check_pre_shutdown_callback_order) {
auto context = std::make_shared<rclcpp::Context>();
context->init(0, nullptr);
int result[4] = {0, 0, 0, 0};
int index = 0;
auto callback1 = [&result, &index]() {
result[index] = 1;
index++;
};
auto callback2 = [&result, &index]() {
result[index] = 2;
index++;
};
auto callback3 = [&result, &index]() {
result[index] = 3;
index++;
};
auto callback4 = [&result, &index]() {
result[index] = 4;
index++;
};
context->add_pre_shutdown_callback(callback1);
context->add_pre_shutdown_callback(callback2);
context->add_pre_shutdown_callback(callback3);
context->add_pre_shutdown_callback(callback4);
context->shutdown("for test");
EXPECT_TRUE(result[0] == 1 && result[1] == 2 && result[2] == 3 && result[3] == 4);
}
TEST(TestContext, check_on_shutdown_callback_order) {
auto context = std::make_shared<rclcpp::Context>();
context->init(0, nullptr);
int result[4] = {0, 0, 0, 0};
int index = 0;
auto callback1 = [&result, &index]() {
result[index] = 1;
index++;
};
auto callback2 = [&result, &index]() {
result[index] = 2;
index++;
};
auto callback3 = [&result, &index]() {
result[index] = 3;
index++;
};
auto callback4 = [&result, &index]() {
result[index] = 4;
index++;
};
context->add_on_shutdown_callback(callback1);
context->add_on_shutdown_callback(callback2);
context->add_on_shutdown_callback(callback3);
context->add_on_shutdown_callback(callback4);
context->shutdown("for test");
EXPECT_TRUE(result[0] == 1 && result[1] == 2 && result[2] == 3 && result[3] == 4);
}
TEST(TestContext, check_mixed_register_shutdown_callback_order) {
auto context = std::make_shared<rclcpp::Context>();
context->init(0, nullptr);
int result[8] = {0, 0, 0, 0, 0, 0, 0, 0};
int index = 0;
auto callback1 = [&result, &index]() {
result[index] = 1;
index++;
};
auto callback2 = [&result, &index]() {
result[index] = 2;
index++;
};
auto callback3 = [&result, &index]() {
result[index] = 3;
index++;
};
auto callback4 = [&result, &index]() {
result[index] = 4;
index++;
};
auto callback5 = [&result, &index]() {
result[index] = 5;
index++;
};
auto callback6 = [&result, &index]() {
result[index] = 6;
index++;
};
auto callback7 = [&result, &index]() {
result[index] = 7;
index++;
};
auto callback8 = [&result, &index]() {
result[index] = 8;
index++;
};
// Mixed register
context->add_pre_shutdown_callback(callback1);
context->add_on_shutdown_callback(callback5);
context->add_pre_shutdown_callback(callback2);
context->add_on_shutdown_callback(callback6);
context->add_pre_shutdown_callback(callback3);
context->add_on_shutdown_callback(callback7);
context->add_pre_shutdown_callback(callback4);
context->add_on_shutdown_callback(callback8);
context->shutdown("for test");
EXPECT_TRUE(
result[0] == 1 && result[1] == 2 && result[2] == 3 && result[3] == 4 &&
result[4] == 5 && result[5] == 6 && result[6] == 7 && result[7] == 8);
}
TEST(TestContext, check_pre_shutdown_callback_order_after_del) {
auto context = std::make_shared<rclcpp::Context>();
context->init(0, nullptr);
int result[4] = {0, 0, 0, 0};
int index = 0;
auto callback1 = [&result, &index]() {
result[index] = 1;
index++;
};
auto callback2 = [&result, &index]() {
result[index] = 2;
index++;
};
auto callback3 = [&result, &index]() {
result[index] = 3;
index++;
};
auto callback4 = [&result, &index]() {
result[index] = 4;
index++;
};
context->add_pre_shutdown_callback(callback1);
auto callback_handle = context->add_pre_shutdown_callback(callback2);
context->add_pre_shutdown_callback(callback3);
context->add_pre_shutdown_callback(callback4);
EXPECT_TRUE(context->remove_pre_shutdown_callback(callback_handle));
EXPECT_FALSE(context->remove_pre_shutdown_callback(callback_handle));
context->shutdown("for test");
EXPECT_TRUE(result[0] == 1 && result[1] == 3 && result[2] == 4 && result[3] == 0);
}
TEST(TestContext, check_on_shutdown_callback_order_after_del) {
auto context = std::make_shared<rclcpp::Context>();
context->init(0, nullptr);
int result[4] = {0, 0, 0, 0};
int index = 0;
auto callback1 = [&result, &index]() {
result[index] = 1;
index++;
};
auto callback2 = [&result, &index]() {
result[index] = 2;
index++;
};
auto callback3 = [&result, &index]() {
result[index] = 3;
index++;
};
auto callback4 = [&result, &index]() {
result[index] = 4;
index++;
};
context->add_on_shutdown_callback(callback1);
auto callback_handle = context->add_on_shutdown_callback(callback2);
context->add_on_shutdown_callback(callback3);
context->add_on_shutdown_callback(callback4);
EXPECT_TRUE(context->remove_on_shutdown_callback(callback_handle));
EXPECT_FALSE(context->remove_on_shutdown_callback(callback_handle));
context->shutdown("for test");
EXPECT_TRUE(result[0] == 1 && result[1] == 3 && result[2] == 4 && result[3] == 0);
}

View File

@@ -0,0 +1,180 @@
// Copyright 2021 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <gtest/gtest.h>
#include <chrono>
#include <memory>
#include <string>
#include "rclcpp/rclcpp.hpp"
#include "rcl_interfaces/msg/log.hpp"
using namespace std::chrono_literals;
class TestRosoutSubscription : public ::testing::Test
{
protected:
static void SetUpTestCase()
{
rclcpp::init(0, nullptr);
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
void SetUp()
{
node = std::make_shared<rclcpp::Node>("test_rosout_subscription", "/ns");
sub = node->create_subscription<rcl_interfaces::msg::Log>(
"/rosout", 10, [this](rcl_interfaces::msg::Log::ConstSharedPtr msg) {
if (msg->msg == this->rosout_msg_data &&
msg->name == this->rosout_msg_name)
{
received_msg_promise.set_value(true);
}
});
}
void TearDown()
{
node.reset();
}
rclcpp::Node::SharedPtr node;
rclcpp::Subscription<rcl_interfaces::msg::Log>::SharedPtr sub;
std::promise<bool> received_msg_promise;
std::string rosout_msg_data;
std::string rosout_msg_name;
};
TEST_F(TestRosoutSubscription, test_rosoutsubscription_getchild) {
std::string logger_name = "ns.test_rosout_subscription.child";
this->rosout_msg_data = "SOMETHING";
this->rosout_msg_name = logger_name;
{
// before calling get_child of Logger
{
RCLCPP_INFO(
rclcpp::get_logger(logger_name), this->rosout_msg_data.c_str());
auto future = received_msg_promise.get_future();
auto return_code = rclcpp::spin_until_future_complete(this->node, future, 3s);
ASSERT_EQ(rclcpp::FutureReturnCode::TIMEOUT, return_code);
received_msg_promise = {};
}
rclcpp::Logger child_logger = this->node->get_logger().get_child("child");
ASSERT_EQ(child_logger.get_name(), logger_name);
// after calling get_child of Logger
// 1. use child_logger directly
{
RCLCPP_INFO(child_logger, this->rosout_msg_data.c_str());
auto future = received_msg_promise.get_future();
auto return_code = rclcpp::spin_until_future_complete(this->node, future, 3s);
ASSERT_EQ(rclcpp::FutureReturnCode::SUCCESS, return_code);
EXPECT_TRUE(future.get());
received_msg_promise = {};
}
// 2. use rclcpp::get_logger
{
RCLCPP_INFO(rclcpp::get_logger(logger_name), this->rosout_msg_data.c_str());
auto future = received_msg_promise.get_future();
auto return_code = rclcpp::spin_until_future_complete(this->node, future, 3s);
ASSERT_EQ(rclcpp::FutureReturnCode::SUCCESS, return_code);
EXPECT_TRUE(future.get());
received_msg_promise = {};
}
}
// `child_logger` is end of life, there is no sublogger
{
RCLCPP_INFO(rclcpp::get_logger(logger_name), this->rosout_msg_data.c_str());
auto future = received_msg_promise.get_future();
auto return_code = rclcpp::spin_until_future_complete(this->node, future, 3s);
ASSERT_EQ(rclcpp::FutureReturnCode::TIMEOUT, return_code);
received_msg_promise = {};
}
}
TEST_F(TestRosoutSubscription, test_rosoutsubscription_parent_log) {
std::string logger_name = "ns.test_rosout_subscription";
this->rosout_msg_data = "SOMETHING";
this->rosout_msg_name = logger_name;
rclcpp::Logger logger = this->node->get_logger();
ASSERT_EQ(logger.get_name(), logger_name);
RCLCPP_INFO(logger, this->rosout_msg_data.c_str());
auto future = received_msg_promise.get_future();
auto return_code = rclcpp::spin_until_future_complete(this->node, future, 3s);
ASSERT_EQ(rclcpp::FutureReturnCode::SUCCESS, return_code);
EXPECT_TRUE(future.get());
received_msg_promise = {};
}
TEST_F(TestRosoutSubscription, test_rosoutsubscription_child_log) {
std::string logger_name = "ns.test_rosout_subscription.child1";
this->rosout_msg_data = "SOMETHING";
this->rosout_msg_name = logger_name;
rclcpp::Logger logger = this->node->get_logger();
RCLCPP_INFO(logger, this->rosout_msg_data.c_str());
auto future = received_msg_promise.get_future();
auto return_code = rclcpp::spin_until_future_complete(this->node, future, 3s);
ASSERT_EQ(rclcpp::FutureReturnCode::TIMEOUT, return_code);
received_msg_promise = {};
logger = this->node->get_logger().get_child("child1");
RCLCPP_INFO(logger, this->rosout_msg_data.c_str());
future = received_msg_promise.get_future();
return_code = rclcpp::spin_until_future_complete(this->node, future, 3s);
ASSERT_EQ(rclcpp::FutureReturnCode::SUCCESS, return_code);
EXPECT_TRUE(future.get());
received_msg_promise = {};
logger = this->node->get_logger().get_child("child2");
RCLCPP_INFO(logger, this->rosout_msg_data.c_str());
future = received_msg_promise.get_future();
return_code = rclcpp::spin_until_future_complete(this->node, future, 3s);
ASSERT_EQ(rclcpp::FutureReturnCode::TIMEOUT, return_code);
received_msg_promise = {};
this->rosout_msg_name = "ns.test_rosout_subscription.child2";
RCLCPP_INFO(logger, this->rosout_msg_data.c_str());
future = received_msg_promise.get_future();
return_code = rclcpp::spin_until_future_complete(this->node, future, 3s);
ASSERT_EQ(rclcpp::FutureReturnCode::SUCCESS, return_code);
EXPECT_TRUE(future.get());
received_msg_promise = {};
}
TEST_F(TestRosoutSubscription, test_rosoutsubscription_getchild_hierarchy) {
std::string logger_name = "ns.test_rosout_subscription.child.grandchild";
this->rosout_msg_data = "SOMETHING";
this->rosout_msg_name = logger_name;
rclcpp::Logger grandchild_logger =
this->node->get_logger().get_child("child").get_child("grandchild");
ASSERT_EQ(grandchild_logger.get_name(), logger_name);
RCLCPP_INFO(grandchild_logger, this->rosout_msg_data.c_str());
auto future = received_msg_promise.get_future();
auto return_code = rclcpp::spin_until_future_complete(this->node, future, 3s);
ASSERT_EQ(rclcpp::FutureReturnCode::SUCCESS, return_code);
EXPECT_TRUE(future.get());
received_msg_promise = {};
}

View File

@@ -3,6 +3,15 @@ Changelog for package rclcpp_action
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
19.2.0 (2023-02-24)
-------------------
19.1.0 (2023-02-14)
-------------------
19.0.0 (2023-01-30)
-------------------
18.0.0 (2022-12-29)
-------------------
* Explicitly set callback type (`#2059 <https://github.com/ros2/rclcpp/issues/2059>`_)

View File

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

View File

@@ -2,6 +2,17 @@
Changelog for package rclcpp_components
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
19.2.0 (2023-02-24)
-------------------
19.1.0 (2023-02-14)
-------------------
19.0.0 (2023-01-30)
-------------------
* Improve component_manager_isolated shutdown (`#2085 <https://github.com/ros2/rclcpp/issues/2085>`_)
* Contributors: Michael Carroll
18.0.0 (2022-12-29)
-------------------
* Update maintainers (`#2043 <https://github.com/ros2/rclcpp/issues/2043>`_)

View File

@@ -38,6 +38,16 @@ class ComponentManagerIsolated : public rclcpp_components::ComponentManager
{
std::shared_ptr<rclcpp::Executor> executor;
std::thread thread;
std::atomic_bool thread_initialized;
/// Constructor for the wrapper.
/// This is necessary as atomic variables don't have copy/move operators
/// implemented so this structure is not copyable/movable by default
explicit DedicatedExecutorWrapper(std::shared_ptr<rclcpp::Executor> exec)
: executor(exec),
thread_initialized(false)
{
}
};
public:
@@ -59,15 +69,19 @@ protected:
void
add_node_to_executor(uint64_t node_id) override
{
DedicatedExecutorWrapper executor_wrapper;
auto exec = std::make_shared<ExecutorT>();
exec->add_node(node_wrappers_[node_id].get_node_base_interface());
executor_wrapper.executor = exec;
executor_wrapper.thread = std::thread(
[exec]() {
// Emplace rather than std::move since move operations are deleted for atomics
auto result = dedicated_executor_wrappers_.emplace(std::make_pair(node_id, exec));
DedicatedExecutorWrapper & wrapper = result.first->second;
wrapper.executor = exec;
auto & thread_initialized = wrapper.thread_initialized;
wrapper.thread = std::thread(
[exec, &thread_initialized]() {
thread_initialized = true;
exec->spin();
});
dedicated_executor_wrappers_[node_id] = std::move(executor_wrapper);
}
/// Remove component node from executor model, it's invoked in on_unload_node()
/**
@@ -90,15 +104,21 @@ private:
*/
void cancel_executor(DedicatedExecutorWrapper & executor_wrapper)
{
// We can't immediately call the cancel() API on an executor because if it is not
// already spinning, this operation will have no effect.
// We rely on the assumption that this class creates executors and then immediately makes them
// spin in a separate thread, i.e. the time gap between when the executor is created and when
// it starts to spin is small (although it's not negligible).
// Verify that the executor thread has begun spinning.
// If it has not, then wait until the thread starts to ensure
// that cancel() will fully stop the execution
//
// This prevents a previous race condition that occurs between the
// creation of the executor spin thread and cancelling an executor
while (!executor_wrapper.executor->is_spinning()) {
// This is an arbitrarily small delay to avoid busy looping
rclcpp::sleep_for(std::chrono::milliseconds(1));
if (!executor_wrapper.thread_initialized) {
auto context = this->get_node_base_interface()->get_context();
// Guarantee that either the executor is spinning or we are shutting down.
while (!executor_wrapper.executor->is_spinning() && rclcpp::ok(context)) {
// This is an arbitrarily small delay to avoid busy looping
rclcpp::sleep_for(std::chrono::milliseconds(1));
}
}
// After the while loop we are sure that the executor is now spinning, so

View File

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

View File

@@ -3,6 +3,15 @@ Changelog for package rclcpp_lifecycle
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
19.2.0 (2023-02-24)
-------------------
19.1.0 (2023-02-14)
-------------------
19.0.0 (2023-01-30)
-------------------
18.0.0 (2022-12-29)
-------------------
* Implement Unified Node Interface (NodeInterfaces class) (`#2041 <https://github.com/ros2/rclcpp/issues/2041>`_)

View File

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