Compare commits

...

11 Commits

Author SHA1 Message Date
Yadunund
052c075052 21.0.2 2023-07-14 01:21:56 +08:00
Yadunund
193c252036 Update Changelogs
Signed-off-by: Yadunund <yadunund@openrobotics.org>
2023-07-14 01:21:40 +08:00
mergify[bot]
efbb9b6c89 warning: comparison of integer expressions of different signedness (#2219) (#2222)
https://github.com/ros2/rclcpp/pull/2167#issuecomment-1597197552

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

Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2023-06-23 13:00:42 -04:00
mergify[bot]
3506dd1227 Fix race condition in events-executor (#2177) (#2191)
The initial implementation of the events-executor contained a bug where the executor
would end up in an inconsistent state and stop processing interrupt/shutdown notifications.
Manually adding a node to the executor results in a) producing a notify waitable event
and b) refreshing the executor collections.
The inconsistent state would happen if the event was processed before the collections were
finished to be refreshed: the executor would pick up the event but be unable to process it.
This would leave the `notify_waitable_event_pushed_` flag to true, preventing additional
notify waitable events to be pushed.
The behavior is observable only under heavy load.

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>
(cherry picked from commit 6e1fea14e1)

Co-authored-by: Alberto Soragna <alberto.soragna@gmail.com>
2023-05-31 08:15:45 -05:00
Chris Lalancette
7a837496bd 21.0.1 2023-05-11 14:00:42 +00:00
Chris Lalancette
cd784f6612 Changelog.
Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2023-05-11 14:00:36 +00:00
mergify[bot]
7aa390d5b1 Fix delivered message kind (#2175) (#2178)
Signed-off-by: methylDragon <methylDragon@gmail.com>
(cherry picked from commit 6ca1023ef7)

Co-authored-by: methylDragon <methylDragon@gmail.com>
2023-05-02 11:21:27 -04:00
Chris Lalancette
a431256383 21.0.0 2023-04-18 15:35:47 +00:00
Chris Lalancette
9d2849cb0a Changelog.
Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2023-04-18 15:35:41 +00:00
Lei Liu
3610b68348 Add support for logging service. (#2122)
* Add support for logging service.

* Update to not modify interfaces and not change time_source

* Use unique_ptr for NodeBuiltinExecutorImpl

* Use user thread to run logger service

* Update code for lifecycle_node

Signed-off-by: Barry Xu <barry.xu@sony.com>
Signed-off-by: Lei Liu <Lei.Liu.AP@sony.com>
2023-04-18 11:30:00 -04:00
Michael Carroll
9c03a463c1 Picking ABI-incompatible executor changes (#2170)
* Picking ABI-incompatible executor changes

* Add PIMPL

Signed-off-by: Michael Carroll <mjcarroll@intrinsic.ai>
2023-04-18 11:29:30 -04:00
29 changed files with 661 additions and 143 deletions

View File

@@ -2,6 +2,26 @@
Changelog for package rclcpp
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
21.0.2 (2023-07-14)
-------------------
* Fix warnings related to comparison of integer expressions of different signedness (`#2222 <https://github.com/ros2/rclcpp/issues/2222>`_)
* Fix race condition in events-executor (`#2191 <https://github.com/ros2/rclcpp/issues/2191>`_)
* Contributors: Alberto Soragna, Tomoya Fujita
21.0.1 (2023-05-11)
-------------------
* Fix delivered message kind (`#2175 <https://github.com/ros2/rclcpp/issues/2175>`_) (`#2178 <https://github.com/ros2/rclcpp/issues/2178>`_)
* Contributors: mergify[bot]
21.0.0 (2023-04-18)
-------------------
* Add support for logging service. (`#2122 <https://github.com/ros2/rclcpp/issues/2122>`_)
* Picking ABI-incompatible executor changes (`#2170 <https://github.com/ros2/rclcpp/issues/2170>`_)
* add events-executor and timers-manager in rclcpp (`#2155 <https://github.com/ros2/rclcpp/issues/2155>`_)
* Create common structures for executors to use (`#2143 <https://github.com/ros2/rclcpp/issues/2143>`_)
* Implement deliver message kind (`#2168 <https://github.com/ros2/rclcpp/issues/2168>`_)
* Contributors: Alberto Soragna, Lei Liu, Michael Carroll, methylDragon
20.0.0 (2023-04-13)
-------------------
* applied tracepoints for ring_buffer (`#2091 <https://github.com/ros2/rclcpp/issues/2091>`_)

View File

@@ -180,6 +180,13 @@ public:
return _find_ptrs_if_impl<rclcpp::Waitable, Function>(func, waitable_ptrs_);
}
/// Get the total number of entities in this callback group.
/**
* \return the number of entities in the callback group.
*/
RCLCPP_PUBLIC
size_t size() const;
RCLCPP_PUBLIC
std::atomic_bool &
can_be_taken_from();

View File

@@ -51,6 +51,7 @@ typedef std::map<rclcpp::CallbackGroup::WeakPtr,
// Forward declaration is used in convenience method signature.
class Node;
class ExecutorImplementation;
/// Coordinate the order and timing of available communication tasks.
/**
@@ -697,6 +698,9 @@ protected:
/// shutdown callback handle registered to Context
rclcpp::OnShutdownCallbackHandle shutdown_callback_handle_;
/// Pointer to implementation
std::unique_ptr<ExecutorImplementation> impl_;
};
} // namespace rclcpp

View File

@@ -198,14 +198,15 @@ build_entities_collection(
*
* \param[in] collection Collection of entities corresponding to the current wait set.
* \param[in] wait_result Result of rclcpp::WaitSet::wait corresponding to the collection.
* \return A queue of executables that have been marked ready by the waitset.
* \param[inout] queue of executables to append new ready executables to
* \return number of new ready executables
*/
std::deque<rclcpp::AnyExecutable>
size_t
ready_executables(
const ExecutorEntitiesCollection & collection,
rclcpp::WaitResult<rclcpp::WaitSet> & wait_result
rclcpp::WaitResult<rclcpp::WaitSet> & wait_result,
std::deque<rclcpp::AnyExecutable> & executables
);
} // namespace executors
} // namespace rclcpp

View File

@@ -243,6 +243,11 @@ private:
std::function<void(size_t, int)>
create_waitable_callback(const rclcpp::Waitable * waitable_id);
/// Utility to add the notify waitable to an entities collection
void
add_notify_waitable_to_collection(
rclcpp::executors::ExecutorEntitiesCollection::WaitableCollection & collection);
/// Searches for the provided entity_id in the collection and returns the entity if valid
template<typename CollectionType>
typename CollectionType::EntitySharedPtr

View File

@@ -23,6 +23,9 @@
#include "rclcpp/node_interfaces/node_logging_interface.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rcl_interfaces/srv/get_logger_levels.hpp"
#include "rcl_interfaces/srv/set_logger_levels.hpp"
namespace rclcpp
{
namespace node_interfaces
@@ -35,7 +38,7 @@ public:
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeLoggingInterface)
RCLCPP_PUBLIC
explicit NodeLogging(rclcpp::node_interfaces::NodeBaseInterface * node_base);
explicit NodeLogging(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base);
RCLCPP_PUBLIC
virtual
@@ -49,13 +52,21 @@ public:
const char *
get_logger_name() const override;
RCLCPP_PUBLIC
void
create_logger_services(
node_interfaces::NodeServicesInterface::SharedPtr node_services) override;
private:
RCLCPP_DISABLE_COPY(NodeLogging)
/// Handle to the NodeBaseInterface given in the constructor.
rclcpp::node_interfaces::NodeBaseInterface * node_base_;
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base_;
rclcpp::Logger logger_;
rclcpp::Service<rcl_interfaces::srv::GetLoggerLevels>::SharedPtr get_loggers_service_;
rclcpp::Service<rcl_interfaces::srv::SetLoggerLevels>::SharedPtr set_loggers_service_;
};
} // namespace node_interfaces

View File

@@ -19,6 +19,7 @@
#include "rclcpp/logger.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_interfaces/node_services_interface.hpp"
#include "rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp"
#include "rclcpp/visibility_control.hpp"
@@ -54,6 +55,13 @@ public:
virtual
const char *
get_logger_name() const = 0;
/// create logger services
RCLCPP_PUBLIC
virtual
void
create_logger_services(
node_interfaces::NodeServicesInterface::SharedPtr node_services) = 0;
};
} // namespace node_interfaces

View File

@@ -50,6 +50,7 @@ public:
* - clock_type = RCL_ROS_TIME
* - clock_qos = rclcpp::ClockQoS()
* - use_clock_thread = true
* - enable_logger_service = false
* - rosout_qos = rclcpp::RosoutQoS()
* - parameter_event_qos = rclcpp::ParameterEventQoS
* - with history setting and depth from rmw_qos_profile_parameter_events
@@ -232,6 +233,24 @@ public:
NodeOptions &
start_parameter_services(bool start_parameter_services);
/// Return the enable_logger_service flag.
RCLCPP_PUBLIC
bool
enable_logger_service() const;
/// Set the enable_logger_service flag, return this for logger idiom.
/**
* If true, ROS services are created to allow external nodes to get
* and set logger levels of this node.
*
* If false, loggers will still be configured and set logger levels locally,
* but logger levels cannot be changed remotely .
*
*/
RCLCPP_PUBLIC
NodeOptions &
enable_logger_service(bool enable_log_service);
/// Return the start_parameter_event_publisher flag.
RCLCPP_PUBLIC
bool
@@ -421,6 +440,8 @@ private:
bool use_clock_thread_ {true};
bool enable_logger_service_ {false};
rclcpp::QoS parameter_event_qos_ = rclcpp::ParameterEventsQoS(
rclcpp::QoSInitialization::from_rmw(rmw_qos_profile_parameter_events)
);

View File

@@ -260,13 +260,13 @@ public:
bool
is_serialized() const;
/// Return the type of the subscription.
/// Return the delivered message kind.
/**
* \return `DeliveredMessageKind`, which adjusts how messages are received and delivered.
*/
RCLCPP_PUBLIC
DeliveredMessageKind
get_subscription_type() const;
get_delivered_message_kind() const;
/// Get matching publisher count.
/** \return The number of publishers on this topic. */
@@ -663,7 +663,7 @@ private:
RCLCPP_DISABLE_COPY(SubscriptionBase)
rosidl_message_type_support_t type_support_;
DeliveredMessageKind delivered_message_type_;
DeliveredMessageKind delivered_message_kind_;
std::atomic<bool> subscription_in_use_by_wait_set_{false};
std::atomic<bool> intra_process_subscription_waitable_in_use_by_wait_set_{false};

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

View File

@@ -56,6 +56,16 @@ CallbackGroup::type() const
return type_;
}
size_t
CallbackGroup::size() const
{
return
subscription_ptrs_.size() +
service_ptrs_.size() +
client_ptrs_.size() +
timer_ptrs_.size() +
waitable_ptrs_.size();
}
void CallbackGroup::collect_all_ptrs(
std::function<void(const rclcpp::SubscriptionBase::SharedPtr &)> sub_func,
std::function<void(const rclcpp::ServiceBase::SharedPtr &)> service_func,

View File

@@ -41,11 +41,14 @@ using namespace std::chrono_literals;
using rclcpp::exceptions::throw_from_rcl_error;
using rclcpp::Executor;
class rclcpp::ExecutorImplementation {};
Executor::Executor(const rclcpp::ExecutorOptions & options)
: spinning(false),
interrupt_guard_condition_(std::make_shared<rclcpp::GuardCondition>(options.context)),
shutdown_guard_condition_(std::make_shared<rclcpp::GuardCondition>(options.context)),
memory_strategy_(options.memory_strategy)
memory_strategy_(options.memory_strategy),
impl_(std::make_unique<rclcpp::ExecutorImplementation>())
{
// Store the context for later use.
context_ = options.context;
@@ -600,7 +603,7 @@ Executor::execute_subscription(rclcpp::SubscriptionBase::SharedPtr subscription)
rclcpp::MessageInfo message_info;
message_info.get_rmw_message_info().from_intra_process = false;
switch (subscription->get_subscription_type()) {
switch (subscription->get_delivered_message_kind()) {
// Deliver ROS message
case rclcpp::DeliveredMessageKind::ROS_MESSAGE:
{

View File

@@ -20,12 +20,13 @@ namespace executors
{
bool ExecutorEntitiesCollection::empty() const
{
return subscriptions.empty() &&
timers.empty() &&
guard_conditions.empty() &&
clients.empty() &&
services.empty() &&
waitables.empty();
return
subscriptions.empty() &&
timers.empty() &&
guard_conditions.empty() &&
clients.empty() &&
services.empty() &&
waitables.empty();
}
void ExecutorEntitiesCollection::clear()
@@ -38,7 +39,6 @@ void ExecutorEntitiesCollection::clear()
waitables.clear();
}
void
build_entities_collection(
const std::vector<rclcpp::CallbackGroup::WeakPtr> & callback_groups,
@@ -94,109 +94,136 @@ build_entities_collection(
}
}
template<typename EntityCollectionType>
void check_ready(
EntityCollectionType & collection,
std::deque<rclcpp::AnyExecutable> & executables,
size_t size_of_waited_entities,
typename EntityCollectionType::Key * waited_entities,
std::function<bool(rclcpp::AnyExecutable &,
typename EntityCollectionType::EntitySharedPtr &)> fill_executable)
size_t
ready_executables(
const ExecutorEntitiesCollection & collection,
rclcpp::WaitResult<rclcpp::WaitSet> & wait_result,
std::deque<rclcpp::AnyExecutable> & executables
)
{
for (size_t ii = 0; ii < size_of_waited_entities; ++ii) {
if (!waited_entities[ii]) {continue;}
auto entity_iter = collection.find(waited_entities[ii]);
if (entity_iter != collection.end()) {
size_t added = 0;
if (wait_result.kind() != rclcpp::WaitResultKind::Ready) {
return added;
}
auto rcl_wait_set = wait_result.get_wait_set().get_rcl_wait_set();
// Cache shared pointers to groups to avoid extra work re-locking them
std::map<rclcpp::CallbackGroup::WeakPtr,
rclcpp::CallbackGroup::SharedPtr,
std::owner_less<rclcpp::CallbackGroup::WeakPtr>> group_map;
auto group_cache = [&group_map](const rclcpp::CallbackGroup::WeakPtr & weak_cbg_ptr)
{
if (group_map.count(weak_cbg_ptr) == 0) {
group_map.insert({weak_cbg_ptr, weak_cbg_ptr.lock()});
}
return group_map.find(weak_cbg_ptr)->second;
};
for (size_t ii = 0; ii < rcl_wait_set.size_of_timers; ++ii) {
if (nullptr == rcl_wait_set.timers[ii]) {continue;}
auto entity_iter = collection.timers.find(rcl_wait_set.timers[ii]);
if (entity_iter != collection.timers.end()) {
auto entity = entity_iter->second.entity.lock();
if (!entity) {
continue;
}
auto callback_group = entity_iter->second.callback_group.lock();
if (callback_group && !callback_group->can_be_taken_from().load()) {
auto group_info = group_cache(entity_iter->second.callback_group);
if (group_info && !group_info->can_be_taken_from().load()) {
continue;
}
if (!entity->call()) {
continue;
}
rclcpp::AnyExecutable exec;
exec.callback_group = callback_group;
if (fill_executable(exec, entity)) {
executables.push_back(exec);
}
exec.timer = entity;
exec.callback_group = group_info;
executables.push_back(exec);
added++;
}
}
}
std::deque<rclcpp::AnyExecutable>
ready_executables(
const ExecutorEntitiesCollection & collection,
rclcpp::WaitResult<rclcpp::WaitSet> & wait_result
)
{
std::deque<rclcpp::AnyExecutable> ret;
if (wait_result.kind() != rclcpp::WaitResultKind::Ready) {
return ret;
}
auto rcl_wait_set = wait_result.get_wait_set().get_rcl_wait_set();
check_ready(
collection.timers,
ret,
rcl_wait_set.size_of_timers,
rcl_wait_set.timers,
[](rclcpp::AnyExecutable & exec, auto timer) {
if (!timer->call()) {
return false;
for (size_t ii = 0; ii < rcl_wait_set.size_of_subscriptions; ++ii) {
if (nullptr == rcl_wait_set.subscriptions[ii]) {continue;}
auto entity_iter = collection.subscriptions.find(rcl_wait_set.subscriptions[ii]);
if (entity_iter != collection.subscriptions.end()) {
auto entity = entity_iter->second.entity.lock();
if (!entity) {
continue;
}
exec.timer = timer;
return true;
});
auto group_info = group_cache(entity_iter->second.callback_group);
if (group_info && !group_info->can_be_taken_from().load()) {
continue;
}
rclcpp::AnyExecutable exec;
exec.subscription = entity;
exec.callback_group = group_info;
executables.push_back(exec);
added++;
}
}
check_ready(
collection.subscriptions,
ret,
rcl_wait_set.size_of_subscriptions,
rcl_wait_set.subscriptions,
[](rclcpp::AnyExecutable & exec, auto subscription) {
exec.subscription = subscription;
return true;
});
for (size_t ii = 0; ii < rcl_wait_set.size_of_services; ++ii) {
if (nullptr == rcl_wait_set.services[ii]) {continue;}
auto entity_iter = collection.services.find(rcl_wait_set.services[ii]);
if (entity_iter != collection.services.end()) {
auto entity = entity_iter->second.entity.lock();
if (!entity) {
continue;
}
auto group_info = group_cache(entity_iter->second.callback_group);
if (group_info && !group_info->can_be_taken_from().load()) {
continue;
}
rclcpp::AnyExecutable exec;
exec.service = entity;
exec.callback_group = group_info;
executables.push_back(exec);
added++;
}
}
check_ready(
collection.services,
ret,
rcl_wait_set.size_of_services,
rcl_wait_set.services,
[](rclcpp::AnyExecutable & exec, auto service) {
exec.service = service;
return true;
});
check_ready(
collection.clients,
ret,
rcl_wait_set.size_of_clients,
rcl_wait_set.clients,
[](rclcpp::AnyExecutable & exec, auto client) {
exec.client = client;
return true;
});
for (size_t ii = 0; ii < rcl_wait_set.size_of_clients; ++ii) {
if (nullptr == rcl_wait_set.clients[ii]) {continue;}
auto entity_iter = collection.clients.find(rcl_wait_set.clients[ii]);
if (entity_iter != collection.clients.end()) {
auto entity = entity_iter->second.entity.lock();
if (!entity) {
continue;
}
auto group_info = group_cache(entity_iter->second.callback_group);
if (group_info && !group_info->can_be_taken_from().load()) {
continue;
}
rclcpp::AnyExecutable exec;
exec.client = entity;
exec.callback_group = group_info;
executables.push_back(exec);
added++;
}
}
for (auto & [handle, entry] : collection.waitables) {
auto waitable = entry.entity.lock();
if (waitable && waitable->is_ready(&rcl_wait_set)) {
auto group = entry.callback_group.lock();
if (group && !group->can_be_taken_from().load()) {
continue;
}
rclcpp::AnyExecutable exec;
exec.waitable = waitable;
exec.callback_group = group;
ret.push_back(exec);
if (!waitable) {
continue;
}
if (!waitable->is_ready(&rcl_wait_set)) {
continue;
}
auto group_info = group_cache(entry.callback_group);
if (group_info && !group_info->can_be_taken_from().load()) {
continue;
}
rclcpp::AnyExecutable exec;
exec.waitable = waitable;
exec.callback_group = group_info;
exec.data = waitable->take_data();
executables.push_back(exec);
added++;
}
return ret;
return added;
}
} // namespace executors

View File

@@ -50,6 +50,9 @@ EventsExecutor::EventsExecutor(
timers_manager_ =
std::make_shared<rclcpp::experimental::TimersManager>(context_, timer_on_ready_cb);
this->current_entities_collection_ =
std::make_shared<rclcpp::executors::ExecutorEntitiesCollection>();
notify_waitable_ = std::make_shared<rclcpp::executors::ExecutorNotifyWaitable>(
[this]() {
// This callback is invoked when:
@@ -61,6 +64,10 @@ EventsExecutor::EventsExecutor(
this->refresh_current_collection_from_callback_groups();
});
// Make sure that the notify waitable is immediately added to the collection
// to avoid missing events
this->add_notify_waitable_to_collection(current_entities_collection_->waitables);
notify_waitable_->add_guard_condition(interrupt_guard_condition_);
notify_waitable_->add_guard_condition(shutdown_guard_condition_);
@@ -87,9 +94,6 @@ EventsExecutor::EventsExecutor(
this->entities_collector_ =
std::make_shared<rclcpp::executors::ExecutorEntitiesCollector>(notify_waitable_);
this->current_entities_collection_ =
std::make_shared<rclcpp::executors::ExecutorEntitiesCollection>();
}
EventsExecutor::~EventsExecutor()
@@ -395,18 +399,8 @@ EventsExecutor::refresh_current_collection_from_callback_groups()
// retrieved in the "standard" way.
// To do it, we need to add the notify waitable as an entry in both the new and
// current collections such that it's neither added or removed.
rclcpp::CallbackGroup::WeakPtr weak_group_ptr;
new_collection.waitables.insert(
{
this->notify_waitable_.get(),
{this->notify_waitable_, weak_group_ptr}
});
this->current_entities_collection_->waitables.insert(
{
this->notify_waitable_.get(),
{this->notify_waitable_, weak_group_ptr}
});
this->add_notify_waitable_to_collection(new_collection.waitables);
this->add_notify_waitable_to_collection(current_entities_collection_->waitables);
this->refresh_current_collection(new_collection);
}
@@ -486,3 +480,16 @@ EventsExecutor::create_waitable_callback(const rclcpp::Waitable * entity_key)
};
return callback;
}
void
EventsExecutor::add_notify_waitable_to_collection(
rclcpp::executors::ExecutorEntitiesCollection::WaitableCollection & collection)
{
// The notify waitable is not associated to any group, so use an invalid one
rclcpp::CallbackGroup::WeakPtr weak_group_ptr;
collection.insert(
{
this->notify_waitable_.get(),
{this->notify_waitable_, weak_group_ptr}
});
}

View File

@@ -167,7 +167,7 @@ Node::Node(
options.use_intra_process_comms(),
options.enable_topic_statistics())),
node_graph_(new rclcpp::node_interfaces::NodeGraph(node_base_.get())),
node_logging_(new rclcpp::node_interfaces::NodeLogging(node_base_.get())),
node_logging_(new rclcpp::node_interfaces::NodeLogging(node_base_)),
node_timers_(new rclcpp::node_interfaces::NodeTimers(node_base_.get())),
node_topics_(new rclcpp::node_interfaces::NodeTopics(node_base_.get(), node_timers_.get())),
node_services_(new rclcpp::node_interfaces::NodeServices(node_base_.get())),
@@ -225,6 +225,10 @@ Node::Node(
node_topics_->resolve_topic_name("/parameter_events"),
options.parameter_event_qos(),
rclcpp::detail::PublisherQosParametersTraits{});
if (options.enable_logger_service()) {
node_logging_->create_logger_services(node_services_);
}
}
Node::Node(

View File

@@ -12,11 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "rclcpp/node_impl.hpp"
#include "rclcpp/node_interfaces/node_logging.hpp"
#include "rclcpp/node_interfaces/node_services_interface.hpp"
using rclcpp::node_interfaces::NodeLogging;
NodeLogging::NodeLogging(rclcpp::node_interfaces::NodeBaseInterface * node_base)
NodeLogging::NodeLogging(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base)
: node_base_(node_base)
{
logger_ = rclcpp::get_logger(NodeLogging::get_logger_name());
@@ -37,3 +39,55 @@ NodeLogging::get_logger_name() const
{
return rcl_node_get_logger_name(node_base_->get_rcl_node_handle());
}
void NodeLogging::create_logger_services(
node_interfaces::NodeServicesInterface::SharedPtr node_services)
{
rclcpp::ServicesQoS qos_profile;
const std::string node_name = node_base_->get_name();
auto callback_group = node_base_->get_default_callback_group();
get_loggers_service_ = rclcpp::create_service<rcl_interfaces::srv::GetLoggerLevels>(
node_base_, node_services,
node_name + "/get_logger_levels",
[](
const std::shared_ptr<rmw_request_id_t>,
const std::shared_ptr<rcl_interfaces::srv::GetLoggerLevels::Request> request,
std::shared_ptr<rcl_interfaces::srv::GetLoggerLevels::Response> response)
{
for (auto & name : request->names) {
rcl_interfaces::msg::LoggerLevel logger_level;
logger_level.name = name;
auto ret = rcutils_logging_get_logger_level(name.c_str());
if (ret < 0) {
logger_level.level = 0;
} else {
logger_level.level = static_cast<uint8_t>(ret);
}
response->levels.push_back(std::move(logger_level));
}
},
qos_profile, callback_group);
set_loggers_service_ = rclcpp::create_service<rcl_interfaces::srv::SetLoggerLevels>(
node_base_, node_services,
node_name + "/set_logger_levels",
[](
const std::shared_ptr<rmw_request_id_t>,
const std::shared_ptr<rcl_interfaces::srv::SetLoggerLevels::Request> request,
std::shared_ptr<rcl_interfaces::srv::SetLoggerLevels::Response> response)
{
rcl_interfaces::msg::SetLoggerLevelsResult result;
for (auto & level : request->levels) {
auto ret = rcutils_logging_set_logger_level(level.name.c_str(), level.level);
if (ret != RCUTILS_RET_OK) {
result.successful = false;
result.reason = rcutils_get_error_string().str;
} else {
result.successful = true;
}
response->results.push_back(std::move(result));
}
},
qos_profile, callback_group);
}

View File

@@ -248,6 +248,19 @@ NodeOptions::start_parameter_services(bool start_parameter_services)
return *this;
}
bool
NodeOptions::enable_logger_service() const
{
return this->enable_logger_service_;
}
NodeOptions &
NodeOptions::enable_logger_service(bool enable_logger_service)
{
this->enable_logger_service_ = enable_logger_service;
return *this;
}
bool
NodeOptions::start_parameter_event_publisher() const
{

View File

@@ -52,7 +52,7 @@ SubscriptionBase::SubscriptionBase(
intra_process_subscription_id_(0),
event_callbacks_(event_callbacks),
type_support_(type_support_handle),
delivered_message_type_(delivered_message_kind)
delivered_message_kind_(delivered_message_kind)
{
auto custom_deletor = [node_handle = this->node_handle_](rcl_subscription_t * rcl_subs)
{
@@ -261,13 +261,13 @@ SubscriptionBase::get_message_type_support_handle() const
bool
SubscriptionBase::is_serialized() const
{
return delivered_message_type_ == rclcpp::DeliveredMessageKind::SERIALIZED_MESSAGE;
return delivered_message_kind_ == rclcpp::DeliveredMessageKind::SERIALIZED_MESSAGE;
}
rclcpp::DeliveredMessageKind
SubscriptionBase::get_subscription_type() const
SubscriptionBase::get_delivered_message_kind() const
{
return delivered_message_type_;
return delivered_message_kind_;
}
size_t

View File

@@ -649,6 +649,13 @@ if(TARGET test_wait_for_message)
target_link_libraries(test_wait_for_message ${PROJECT_NAME})
endif()
ament_add_gtest(test_logger_service test_logger_service.cpp)
if(TARGET test_logger_service)
ament_target_dependencies(test_logger_service
"rcl_interfaces")
target_link_libraries(test_logger_service ${PROJECT_NAME})
endif()
ament_add_gtest(test_interface_traits test_interface_traits.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_interface_traits)

View File

@@ -20,12 +20,14 @@
#include <gtest/gtest.h>
#include <algorithm>
#include <atomic>
#include <chrono>
#include <limits>
#include <memory>
#include <string>
#include <thread>
#include <utility>
#include <vector>
#include "rcl/error_handling.h"
#include "rcl/time.h"
@@ -43,18 +45,10 @@ template<typename T>
class TestExecutors : public ::testing::Test
{
public:
static void SetUpTestCase()
{
rclcpp::init(0, nullptr);
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
void SetUp()
{
rclcpp::init(0, nullptr);
const auto test_info = ::testing::UnitTest::GetInstance()->current_test_info();
std::stringstream test_name;
test_name << test_info->test_case_name() << "_" << test_info->name();
@@ -75,6 +69,8 @@ public:
publisher.reset();
subscription.reset();
node.reset();
rclcpp::shutdown();
}
rclcpp::Node::SharedPtr node;
@@ -729,6 +725,77 @@ TYPED_TEST(TestExecutors, testSpinUntilFutureCompleteInterrupted)
spinner.join();
}
// This test verifies that the add_node operation is robust wrt race conditions.
// It's mostly meant to prevent regressions in the events-executor, but the operation should be
// thread-safe in all executor implementations.
// The initial implementation of the events-executor contained a bug where the executor
// would end up in an inconsistent state and stop processing interrupt/shutdown notifications.
// Manually adding a node to the executor results in a) producing a notify waitable event
// and b) refreshing the executor collections.
// The inconsistent state would happen if the event was processed before the collections were
// finished to be refreshed: the executor would pick up the event but be unable to process it.
// This would leave the `notify_waitable_event_pushed_` flag to true, preventing additional
// notify waitable events to be pushed.
// The behavior is observable only under heavy load, so this test spawns several worker
// threads. Due to the nature of the bug, this test may still succeed even if the
// bug is present. However repeated runs will show its flakiness nature and indicate
// an eventual regression.
TYPED_TEST(TestExecutors, testRaceConditionAddNode)
{
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
// Spawn some threads to do some heavy work
std::atomic<bool> should_cancel = false;
std::vector<std::thread> stress_threads;
for (size_t i = 0; i < 5 * std::thread::hardware_concurrency(); i++) {
stress_threads.emplace_back(
[&should_cancel, i]() {
// This is just some arbitrary heavy work
volatile size_t total = 0;
for (size_t k = 0; k < 549528914167; k++) {
if (should_cancel) {
break;
}
total += k * (i + 42);
}
});
}
// Create an executor
auto executor = std::make_shared<ExecutorType>();
// Start spinning
auto executor_thread = std::thread(
[executor]() {
executor->spin();
});
// Add a node to the executor
executor->add_node(this->node);
// Cancel the executor (make sure that it's already spinning first)
while (!executor->is_spinning() && rclcpp::ok()) {
continue;
}
executor->cancel();
// Try to join the thread after cancelling the executor
// This is the "test". We want to make sure that we can still cancel the executor
// regardless of the presence of race conditions
executor_thread.join();
// The test is now completed: we can join the stress threads
should_cancel = true;
for (auto & t : stress_threads) {
t.join();
}
}
// Check spin_until_future_complete with node base pointer (instantiates its own executor)
TEST(TestExecutors, testSpinUntilFutureCompleteNodeBasePtr)
{
@@ -791,7 +858,7 @@ public:
test_name << test_info->test_case_name() << "_" << test_info->name();
node = std::make_shared<rclcpp::Node>("node", test_name.str());
callback_count = 0;
callback_count = 0u;
const std::string topic_name = std::string("topic_") + test_name.str();
@@ -800,7 +867,7 @@ public:
publisher = node->create_publisher<test_msgs::msg::Empty>(topic_name, rclcpp::QoS(1), po);
auto callback = [this](test_msgs::msg::Empty::ConstSharedPtr) {
this->callback_count.fetch_add(1);
this->callback_count.fetch_add(1u);
};
rclcpp::SubscriptionOptions so;
@@ -822,7 +889,7 @@ public:
rclcpp::Node::SharedPtr node;
rclcpp::Publisher<test_msgs::msg::Empty>::SharedPtr publisher;
rclcpp::Subscription<test_msgs::msg::Empty>::SharedPtr subscription;
std::atomic_int callback_count;
std::atomic_size_t callback_count;
};
TYPED_TEST_SUITE(TestIntraprocessExecutors, ExecutorTypes, ExecutorTypeNames);
@@ -838,7 +905,7 @@ TYPED_TEST(TestIntraprocessExecutors, testIntraprocessRetrigger) {
ExecutorType executor;
executor.add_node(this->node);
EXPECT_EQ(0, this->callback_count.load());
EXPECT_EQ(0u, this->callback_count.load());
this->publisher->publish(test_msgs::msg::Empty());
// Wait for up to 5 seconds for the first message to come available.
@@ -852,7 +919,7 @@ TYPED_TEST(TestIntraprocessExecutors, testIntraprocessRetrigger) {
EXPECT_EQ(1u, this->callback_count.load());
// reset counter
this->callback_count.store(0);
this->callback_count.store(0u);
for (size_t ii = 0; ii < kNumMessages; ++ii) {
this->publisher->publish(test_msgs::msg::Empty());

View File

@@ -0,0 +1,214 @@
// 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 <memory>
#include <string>
#include <vector>
#include "rclcpp/rclcpp.hpp"
#include "rclcpp/node.hpp"
#include "rcl_interfaces/srv/get_logger_levels.hpp"
#include "rcl_interfaces/srv/set_logger_levels.hpp"
using namespace std::chrono_literals;
class TestLoggerService : public ::testing::Test
{
protected:
void SetUp()
{
rclcpp::init(0, nullptr);
rclcpp::NodeOptions options = rclcpp::NodeOptions();
options.enable_logger_service(true);
node_ = std::make_shared<rclcpp::Node>("test_logger_service", "/test", options);
}
void TearDown()
{
rclcpp::shutdown();
}
rclcpp::Node::SharedPtr node_;
std::thread thread_;
};
TEST_F(TestLoggerService, check_connect_get_logger_service) {
auto client = node_->create_client<rcl_interfaces::srv::GetLoggerLevels>(
"/test/test_logger_service/get_logger_levels");
ASSERT_TRUE(client->wait_for_service(2s));
}
TEST_F(TestLoggerService, check_connect_set_logger_service) {
auto client = node_->create_client<rcl_interfaces::srv::SetLoggerLevels>(
"/test/test_logger_service/set_logger_levels");
ASSERT_TRUE(client->wait_for_service(2s));
}
TEST_F(TestLoggerService, test_set_and_get_one_logging_level) {
std::string test_logger_name = "rcl";
uint8_t test_logger_level = 20;
{
auto client = node_->create_client<rcl_interfaces::srv::SetLoggerLevels>(
"/test/test_logger_service/set_logger_levels");
ASSERT_TRUE(client->wait_for_service(1s));
auto request = std::make_shared<rcl_interfaces::srv::SetLoggerLevels::Request>();
auto level = rcl_interfaces::msg::LoggerLevel();
level.name = test_logger_name;
level.level = test_logger_level;
request->levels.push_back(level);
auto result = client->async_send_request(request);
ASSERT_EQ(
rclcpp::spin_until_future_complete(node_, result),
rclcpp::FutureReturnCode::SUCCESS);
auto result_get = result.get();
ASSERT_EQ(result_get->results.size(), 1u);
ASSERT_TRUE(result_get->results[0].successful);
ASSERT_STREQ(result_get->results[0].reason.c_str(), "");
}
{
auto client = node_->create_client<rcl_interfaces::srv::GetLoggerLevels>(
"/test/test_logger_service/get_logger_levels");
ASSERT_TRUE(client->wait_for_service(1s));
auto request = std::make_shared<rcl_interfaces::srv::GetLoggerLevels::Request>();
request->names.emplace_back(test_logger_name);
auto result = client->async_send_request(request);
ASSERT_EQ(
rclcpp::spin_until_future_complete(node_, result),
rclcpp::FutureReturnCode::SUCCESS);
auto result_get = result.get();
ASSERT_EQ(result_get->levels.size(), 1u);
ASSERT_STREQ(result_get->levels[0].name.c_str(), test_logger_name.c_str());
ASSERT_EQ(result_get->levels[0].level, test_logger_level);
}
}
TEST_F(TestLoggerService, test_set_and_get_multi_logging_level) {
std::vector<std::pair<std::string, uint8_t>> test_data {
{"rcl", 30},
{"rclcpp", 40},
{"/test/test_logger_service", 50}
};
// Set multi log levels
{
auto client = node_->create_client<rcl_interfaces::srv::SetLoggerLevels>(
"/test/test_logger_service/set_logger_levels");
ASSERT_TRUE(client->wait_for_service(1s));
auto request = std::make_shared<rcl_interfaces::srv::SetLoggerLevels::Request>();
for (auto & set_level : test_data) {
auto level = rcl_interfaces::msg::LoggerLevel();
level.name = std::get<0>(set_level);
level.level = std::get<1>(set_level);
request->levels.push_back(level);
}
auto result = client->async_send_request(request);
ASSERT_EQ(
rclcpp::spin_until_future_complete(node_, result),
rclcpp::FutureReturnCode::SUCCESS);
auto result_get = result.get();
ASSERT_EQ(result_get->results.size(), test_data.size());
for (uint32_t i = 0; i < test_data.size(); i++) {
ASSERT_TRUE(result_get->results[0].successful);
}
}
// Get multi log levels
{
auto client = node_->create_client<rcl_interfaces::srv::GetLoggerLevels>(
"/test/test_logger_service/get_logger_levels");
ASSERT_TRUE(client->wait_for_service(1s));
auto request = std::make_shared<rcl_interfaces::srv::GetLoggerLevels::Request>();
for (auto & set_level : test_data) {
request->names.emplace_back(std::get<0>(set_level));
}
auto result = client->async_send_request(request);
ASSERT_EQ(
rclcpp::spin_until_future_complete(node_, result),
rclcpp::FutureReturnCode::SUCCESS);
auto result_get = result.get();
ASSERT_EQ(result_get->levels.size(), test_data.size());
for (uint32_t i = 0; i < test_data.size(); i++) {
ASSERT_STREQ(result_get->levels[i].name.c_str(), std::get<0>(test_data[i]).c_str());
ASSERT_EQ(result_get->levels[i].level, std::get<1>(test_data[i]));
}
}
}
TEST_F(TestLoggerService, test_set_logging_level_with_invalid_param) {
std::vector<std::pair<std::string, uint8_t>> test_data {
{"rcl", 12},
{"/test/test_logger_service", 22}
};
// Set multi log levels
{
auto client = node_->create_client<rcl_interfaces::srv::SetLoggerLevels>(
"/test/test_logger_service/set_logger_levels");
ASSERT_TRUE(client->wait_for_service(1s));
auto request = std::make_shared<rcl_interfaces::srv::SetLoggerLevels::Request>();
for (auto & set_level : test_data) {
auto level = rcl_interfaces::msg::LoggerLevel();
level.name = std::get<0>(set_level);
level.level = std::get<1>(set_level);
request->levels.push_back(level);
}
auto result = client->async_send_request(request);
ASSERT_EQ(
rclcpp::spin_until_future_complete(node_, result),
rclcpp::FutureReturnCode::SUCCESS);
auto result_get = result.get();
ASSERT_EQ(result_get->results.size(), test_data.size());
for (uint32_t i = 0; i < test_data.size(); i++) {
ASSERT_FALSE(result_get->results[i].successful);
// Check string starts with prefix
ASSERT_EQ(
result_get->results[i].reason.rfind("Unable to determine severity_string for severity", 0),
0);
}
}
}
TEST_F(TestLoggerService, test_set_logging_level_with_partial_invalid_param) {
std::vector<std::pair<std::string, uint8_t>> test_data {
{"rcl", 20},
{"rclcpp", 22},
{"/test/test_logger_service", 30}
};
// Set multi log levels
{
auto client = node_->create_client<rcl_interfaces::srv::SetLoggerLevels>(
"/test/test_logger_service/set_logger_levels");
ASSERT_TRUE(client->wait_for_service(1s));
auto request = std::make_shared<rcl_interfaces::srv::SetLoggerLevels::Request>();
for (auto & set_level : test_data) {
auto level = rcl_interfaces::msg::LoggerLevel();
level.name = std::get<0>(set_level);
level.level = std::get<1>(set_level);
request->levels.push_back(level);
}
auto result = client->async_send_request(request);
ASSERT_EQ(
rclcpp::spin_until_future_complete(node_, result),
rclcpp::FutureReturnCode::SUCCESS);
auto result_get = result.get();
ASSERT_EQ(result_get->results.size(), test_data.size());
ASSERT_TRUE(result_get->results[0].successful);
ASSERT_FALSE(result_get->results[1].successful);
ASSERT_TRUE(result_get->results[2].successful);
}
}

View File

@@ -266,6 +266,11 @@ TEST(TestNodeOptions, bool_setters_and_getters) {
EXPECT_FALSE(options.automatically_declare_parameters_from_overrides());
options.automatically_declare_parameters_from_overrides(true);
EXPECT_TRUE(options.automatically_declare_parameters_from_overrides());
options.enable_logger_service(false);
EXPECT_FALSE(options.enable_logger_service());
options.enable_logger_service(true);
EXPECT_TRUE(options.enable_logger_service());
}
TEST(TestNodeOptions, parameter_event_qos) {

View File

@@ -3,6 +3,15 @@ Changelog for package rclcpp_action
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
21.0.2 (2023-07-14)
-------------------
21.0.1 (2023-05-11)
-------------------
21.0.0 (2023-04-18)
-------------------
20.0.0 (2023-04-13)
-------------------
* extract the result response before the callback is issued. (`#2132 <https://github.com/ros2/rclcpp/issues/2132>`_)

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

View File

@@ -2,6 +2,15 @@
Changelog for package rclcpp_components
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
21.0.2 (2023-07-14)
-------------------
21.0.1 (2023-05-11)
-------------------
21.0.0 (2023-04-18)
-------------------
20.0.0 (2023-04-13)
-------------------
* Update all rclcpp packages to C++17. (`#2121 <https://github.com/ros2/rclcpp/issues/2121>`_)

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

View File

@@ -3,6 +3,18 @@ Changelog for package rclcpp_lifecycle
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
21.0.2 (2023-07-14)
-------------------
21.0.1 (2023-05-11)
-------------------
21.0.0 (2023-04-18)
-------------------
* Add support for logging service. (`#2122 <https://github.com/ros2/rclcpp/issues/2122>`_)
* Support publishing loaned messages in LifecyclePublisher (`#2159 <https://github.com/ros2/rclcpp/issues/2159>`_)
* Contributors: Lei Liu, Michael Babenko
20.0.0 (2023-04-13)
-------------------
* Fixes to silence some clang warnings. (`#2127 <https://github.com/ros2/rclcpp/issues/2127>`_)

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

View File

@@ -76,7 +76,7 @@ LifecycleNode::LifecycleNode(
options.use_intra_process_comms(),
options.enable_topic_statistics())),
node_graph_(new rclcpp::node_interfaces::NodeGraph(node_base_.get())),
node_logging_(new rclcpp::node_interfaces::NodeLogging(node_base_.get())),
node_logging_(new rclcpp::node_interfaces::NodeLogging(node_base_)),
node_timers_(new rclcpp::node_interfaces::NodeTimers(node_base_.get())),
node_topics_(new rclcpp::node_interfaces::NodeTopics(node_base_.get(), node_timers_.get())),
node_services_(new rclcpp::node_interfaces::NodeServices(node_base_.get())),