Compare commits

...

12 Commits

Author SHA1 Message Date
Jacob Perron
2d6db19c0d 0.6.5 2019-12-05 14:08:46 -08:00
Jacob Perron
05a8e5c751 [rclcpp_action] Do not throw exception in action client if take fails (#888) (#905)
As documented in rcl_action, a return code of RCL_RET_ACTION_CLIENT_TAKE_FAILED does not mean that
an error occurred.

Signed-off-by: Jacob Perron <jacob@openrobotics.org>
2019-11-26 12:30:50 -08:00
Steven! Ragnarök
7f23bea7a3 0.6.4 2019-04-05 21:56:38 -07:00
Jacob Perron
f7bb006f75 Backport #637 - Wait for action server before sending goal (#686)
Resolves #638

Co-authored-by: Shane Loretz <shane.loretz@gmail.com>
2019-04-05 14:43:40 -04:00
Steven! Ragnarök
e32d17980d 0.6.3 2019-02-08 10:55:03 -08:00
Chris Lalancette
cd6f195c60 Get parameter map (#575) (crystal-backport) (#619)
* Get parameter map (#575)

* Add in the ability to get parameters in a map.

Any parameters that have a "." in them will be considered to
be part of a "map" (though they can also be get and set
individually).  This PR adds two new template specializations
to the public node API so that it can take a map, and store
the list of values (so setting the parameter with a name of
"foo" and a key of "x" will end up with a parameter of "foo.x").
It also adds an API to get all of the keys corresponding to
a prefix, and returing that as a map (so a get of "foo" will
get all parameters that begin with "foo.").  Note that all
parameters within the map must have the same type, otherwise
an rclcpp::ParameterTypeException will be thrown.

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

* Fix style problems pointed out by uncrustify/cpplint.

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

* Move tests for set_parameter_if_not_set/get_parameter map to rclcpp.

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

* Rename get_parameter -> get_parameters.

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

* Add in documentation from review.

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

* Use list_parameters instead of get_parameters_by_prefix.

This way, we don't break ABI in crystal.

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

* Fix style problems.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2019-01-29 16:05:47 -05:00
Steven! Ragnarök
77a3385473 Merge pull request #616 from ros2/crystal-backport-613
Backport #613 to crystal branch.
2019-01-09 15:50:17 -08:00
Jacob Perron
8b176288e5 Fix errors from uncrustify v0.68 (#613) 2019-01-08 15:18:48 -08:00
Steven! Ragnarök
69d7e69957 0.6.2 2018-12-12 21:56:41 -08:00
William Woodall
2e58dde5ef use signal safe synchronization with platform specific semaphores (#607)
* use signal safe synchronization with platform specific semaphores

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

* addressed feedback and refactored into separate files

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

* Apply suggestions from code review

Co-Authored-By: wjwwood <william+github@osrfoundation.org>

* include what you use (cpplint)

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

* avoid redundant use of SignalHandler::

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

* Update rclcpp/src/rclcpp/signal_handler.hpp

Co-Authored-By: wjwwood <william+github@osrfoundation.org>

* fix Windows build

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

* actually fix Windows

Signed-off-by: William Woodall <william@osrfoundation.org>
2018-12-12 21:12:49 -08:00
Tully Foote
c93beb5d16 Resolve startup race condition for sim time (#608)
Resolves #595 

* Separate the Node Time Source from the Node Clock
* Implement initial value checking of use_sim_time parameter parameter
* Be sure to update all newly attached clocks
* Homogenizing the behavior to use the last received value otherwise zero time when enabling sim time.
* Add virtual destructors to interface classes
2018-12-12 11:52:54 -08:00
William Woodall
a54a329153 defer signal handling to a singleton thread (#605)
* [WIP] Refactor signal handling.

* fix deadlock

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

* finished fixing signal handling and removing more global state

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

* add missing include of <condition_variable>

* use unordered map in signal handling class

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

* use consistent terminology

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

* use emplace in map

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

* avoid throwing in destructor

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

* words

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

* avoid throwing from destructors in a few places

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

* make install/uninstall thread-safe

Signed-off-by: William Woodall <william@osrfoundation.org>
2018-12-11 18:17:26 -08:00
40 changed files with 1349 additions and 352 deletions

View File

@@ -2,6 +2,27 @@
Changelog for package rclcpp
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
0.6.5 (2019-12-05)
------------------
0.6.4 (2019-04-06)
------------------
0.6.3 (2019-02-08)
------------------
* Added the ability to get parameters in a map. (`#575 <https://github.com/ros2/rclcpp/issues/575>`_)
* Backported by (`#619 <https://github.com/ros2/rclcpp/issues/619>`_) for Crystal.
* Fix errors from uncrustify v0.68 (`#613 <https://github.com/ros2/rclcpp/issues/613>`_)
* Backported by `#616 <https://github.com/ros2/rclcpp/issues/616>`_ for Crystal.
* Contributors: Chris Lalancette, Jacob Perron, Steven! Ragnarök
0.6.2 (2018-12-13)
------------------
* Updated to use signal safe synchronization with platform specific semaphores (`#607 <https://github.com/ros2/rclcpp/issues/607>`_)
* Resolved startup race condition for sim time (`#608 <https://github.com/ros2/rclcpp/issues/608>`_)
Resolves `#595 <https://github.com/ros2/rclcpp/issues/595>`_
* Contributors: Tully Foote, William Woodall
0.6.1 (2018-12-07)
------------------
* Added wait_for_action_server() for action clients (`#598 <https://github.com/ros2/rclcpp/issues/598>`_)

View File

@@ -53,6 +53,7 @@ set(${PROJECT_NAME}_SRCS
src/rclcpp/node_interfaces/node_logging.cpp
src/rclcpp/node_interfaces/node_parameters.cpp
src/rclcpp/node_interfaces/node_services.cpp
src/rclcpp/node_interfaces/node_time_source.cpp
src/rclcpp/node_interfaces/node_timers.cpp
src/rclcpp/node_interfaces/node_topics.cpp
src/rclcpp/node_interfaces/node_waitables.cpp
@@ -64,6 +65,7 @@ set(${PROJECT_NAME}_SRCS
src/rclcpp/parameter_service.cpp
src/rclcpp/publisher.cpp
src/rclcpp/service.cpp
src/rclcpp/signal_handler.cpp
src/rclcpp/subscription.cpp
src/rclcpp/time.cpp
src/rclcpp/time_source.cpp
@@ -399,6 +401,17 @@ if(BUILD_TESTING)
"rcl")
target_link_libraries(test_multi_threaded_executor ${PROJECT_NAME})
endif()
ament_add_gtest(test_local_parameters test/test_local_parameters.cpp)
if(TARGET test_local_parameters)
target_include_directories(test_local_parameters PUBLIC
${rcl_interfaces_INCLUDE_DIRS}
${rmw_INCLUDE_DIRS}
${rosidl_generator_cpp_INCLUDE_DIRS}
${rosidl_typesupport_cpp_INCLUDE_DIRS}
)
target_link_libraries(test_local_parameters ${PROJECT_NAME})
endif()
endif()
ament_package(

View File

@@ -15,6 +15,7 @@
#ifndef RCLCPP__CONTEXT_HPP_
#define RCLCPP__CONTEXT_HPP_
#include <condition_variable>
#include <functional>
#include <memory>
#include <mutex>
@@ -26,6 +27,8 @@
#include <vector>
#include "rcl/context.h"
#include "rcl/guard_condition.h"
#include "rcl/wait.h"
#include "rclcpp/init_options.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/visibility_control.hpp"
@@ -147,21 +150,21 @@ public:
* - rcl_shutdown() is called on the internal rcl_context_t instance
* - the shutdown reason is set
* - each on_shutdown callback is called, in the order that they were added
* - if notify_all is true, rclcpp::notify_all is called to unblock some ROS functions
* - interrupt blocking sleep_for() calls, so they return early due to shutdown
* - interrupt blocking executors and wait sets
*
* The underlying rcl context is not finalized by this function.
*
* This function is thread-safe.
*
* \param[in] reason the description of why shutdown happened
* \param[in] notify_all if true, then rclcpp::notify_all will be called
* \return true if shutdown was successful, false if context was already shutdown
* \throw various exceptions derived from RCLErrorBase, if rcl_shutdown fails
*/
RCLCPP_PUBLIC
virtual
bool
shutdown(const std::string & reason, bool notify_all = true);
shutdown(const std::string & reason);
using OnShutdownCallback = std::function<void ()>;
@@ -170,8 +173,9 @@ public:
* These callbacks will be called in the order they are added as the second
* to last step in shutdown().
*
* These callbacks may be run in the signal handler as the signal handler
* may call shutdown() on this context.
* When shutdown occurs due to the signal handler, these callbacks are run
* asynchronoulsy in the dedicated singal handling thread.
*
* Also, shutdown() may be called from the destructor of this function.
* Therefore, it is not safe to throw exceptions from these callbacks.
* Instead, log errors or use some other mechanism to indicate an error has
@@ -211,6 +215,86 @@ public:
std::shared_ptr<rcl_context_t>
get_rcl_context();
/// Sleep for a given period of time or until shutdown() is called.
/**
* This function can be interrupted early if:
*
* - this context is shutdown()
* - this context is destructed (resulting in shutdown)
* - this context has shutdown_on_sigint=true and SIGINT occurs (resulting in shutdown)
* - interrupt_all_sleep_for() is called
*
* \param[in] nanoseconds A std::chrono::duration representing how long to sleep for.
* \return true if the condition variable did not timeout, i.e. you were interrupted.
*/
RCLCPP_PUBLIC
bool
sleep_for(const std::chrono::nanoseconds & nanoseconds);
/// Interrupt any blocking sleep_for calls, causing them to return immediately and return true.
RCLCPP_PUBLIC
virtual
void
interrupt_all_sleep_for();
/// Get a handle to the guard condition which is triggered when interrupted.
/**
* This guard condition is triggered any time interrupt_all_wait_sets() is
* called, which may be called by the user, or shutdown().
* And in turn, shutdown() may be called by the user, the destructor of this
* context, or the signal handler if installed and shutdown_on_sigint is true
* for this context.
*
* The first time that this function is called for a given wait set a new guard
* condition will be created and returned; thereafter the same guard condition
* will be returned for the same wait set.
* This mechanism is designed to ensure that the same guard condition is not
* reused across wait sets (e.g., when using multiple executors in the same
* process).
* This method will throw an exception if initialization of the guard
* condition fails.
*
* The returned guard condition needs to be released with the
* release_interrupt_guard_condition() method in order to reclaim resources.
*
* \param[in] wait_set Pointer to the rcl_wait_set_t that will be using the
* resulting guard condition.
* \return Pointer to the guard condition.
*/
RCLCPP_PUBLIC
rcl_guard_condition_t *
get_interrupt_guard_condition(rcl_wait_set_t * wait_set);
/// Release the previously allocated guard condition which is triggered when interrupted.
/**
* If you previously called get_interrupt_guard_condition() for a given wait
* set to get a interrupt guard condition, then you should call
* release_interrupt_guard_condition() when you're done, to free that
* condition.
* Will throw an exception if get_interrupt_guard_condition() wasn't
* previously called for the given wait set.
*
* After calling this, the pointer returned by get_interrupt_guard_condition()
* for the given wait_set is invalid.
*
* \param[in] wait_set Pointer to the rcl_wait_set_t that was using the
* resulting guard condition.
*/
RCLCPP_PUBLIC
void
release_interrupt_guard_condition(rcl_wait_set_t * wait_set);
/// Nothrow version of release_interrupt_guard_condition(), logs to RCLCPP_ERROR instead.
RCLCPP_PUBLIC
void
release_interrupt_guard_condition(rcl_wait_set_t * wait_set, const std::nothrow_t &) noexcept;
/// Interrupt any blocking executors, or wait sets associated with this context.
RCLCPP_PUBLIC
virtual
void
interrupt_all_wait_sets();
/// Return a singleton instance for the SubContext type, constructing one if necessary.
template<typename SubContext, typename ... Args>
std::shared_ptr<SubContext>
@@ -261,6 +345,16 @@ private:
std::vector<OnShutdownCallback> on_shutdown_callbacks_;
std::mutex on_shutdown_callbacks_mutex_;
/// Condition variable for timed sleep (see sleep_for).
std::condition_variable interrupt_condition_variable_;
/// Mutex for protecting the global condition variable.
std::mutex interrupt_mutex_;
/// Mutex to protect sigint_guard_cond_handles_.
std::mutex interrupt_guard_cond_handles_mutex_;
/// Guard conditions for interrupting of associated wait sets on interrupt_all_wait_sets().
std::unordered_map<rcl_wait_set_t *, rcl_guard_condition_t> interrupt_guard_cond_handles_;
};
/// Return a copy of the list of context shared pointers.

View File

@@ -49,14 +49,14 @@ template<typename FunctionT>
struct function_traits
{
using arguments = typename tuple_tail<
typename function_traits<decltype( & FunctionT::operator())>::arguments>::type;
typename function_traits<decltype( &FunctionT::operator())>::arguments>::type;
static constexpr std::size_t arity = std::tuple_size<arguments>::value;
template<std::size_t N>
using argument_type = typename std::tuple_element<N, arguments>::type;
using return_type = typename function_traits<decltype( & FunctionT::operator())>::return_type;
using return_type = typename function_traits<decltype( &FunctionT::operator())>::return_type;
};
// Free functions

View File

@@ -134,6 +134,12 @@ public:
void
shutdown();
/// Nothrow version of shutdown(), logs to RCLCPP_ERROR instead.
RCLCPP_PUBLIC
virtual
void
shutdown(const std::nothrow_t &) noexcept;
/// Return true if shutdown() has been called, else false.
RCLCPP_PUBLIC
virtual
@@ -155,6 +161,12 @@ protected:
private:
RCLCPP_DISABLE_COPY(GraphListener)
/** \internal */
void
__shutdown(bool);
rclcpp::Context::SharedPtr parent_context_;
std::thread listener_thread_;
bool is_started_;
std::atomic_bool is_shutdown_;

View File

@@ -47,6 +47,7 @@
#include "rclcpp/node_interfaces/node_logging_interface.hpp"
#include "rclcpp/node_interfaces/node_parameters_interface.hpp"
#include "rclcpp/node_interfaces/node_services_interface.hpp"
#include "rclcpp/node_interfaces/node_time_source_interface.hpp"
#include "rclcpp/node_interfaces/node_timers_interface.hpp"
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
#include "rclcpp/node_interfaces/node_waitables_interface.hpp"
@@ -278,6 +279,20 @@ public:
const std::string & name,
const ParameterT & value);
/// Set a map of parameters with the same prefix.
/**
* For each key in the map, a parameter with a name of "name.key" will be set
* to the value in the map.
*
* \param[in] name The prefix of the parameters to set.
* \param[in] values The parameters to set in the given prefix.
*/
template<typename MapValueT>
void
set_parameters_if_not_set(
const std::string & name,
const std::map<std::string, MapValueT> & values);
RCLCPP_PUBLIC
std::vector<rclcpp::Parameter>
get_parameters(const std::vector<std::string> & names) const;
@@ -304,6 +319,24 @@ public:
bool
get_parameter(const std::string & name, ParameterT & parameter) const;
/// Assign the value of the map parameter if set into the values argument.
/**
* Parameter names that are part of a map are of the form "name.member".
* This API gets all parameters that begin with "name", storing them into the
* map with the name of the parameter and their value.
* If there are no members in the named map, then the "values" argument is not changed.
*
* \param[in] name The prefix of the parameters to get.
* \param[out] values The map of output values, with one std::string,MapValueT
* per parameter.
* \returns true if values was changed, false otherwise
*/
template<typename MapValueT>
bool
get_parameters(
const std::string & name,
std::map<std::string, MapValueT> & values) const;
/// Get the parameter value, or the "alternative value" if not set, and assign it to "value".
/**
* If the parameter was not set, then the "value" argument is assigned
@@ -458,6 +491,11 @@ public:
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr
get_node_parameters_interface();
/// Return the Node's internal NodeParametersInterface implementation.
RCLCPP_PUBLIC
rclcpp::node_interfaces::NodeTimeSourceInterface::SharedPtr
get_node_time_source_interface();
private:
RCLCPP_DISABLE_COPY(Node)
@@ -473,6 +511,7 @@ private:
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services_;
rclcpp::node_interfaces::NodeClockInterface::SharedPtr node_clock_;
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters_;
rclcpp::node_interfaces::NodeTimeSourceInterface::SharedPtr node_time_source_;
rclcpp::node_interfaces::NodeWaitablesInterface::SharedPtr node_waitables_;
bool use_intra_process_comms_;

View File

@@ -224,6 +224,28 @@ Node::set_parameter_if_not_set(
}
}
// this is a partially-specialized version of set_parameter_if_not_set above,
// where our concrete type for ParameterT is std::map, but the to-be-determined
// type is the value in the map.
template<typename MapValueT>
void
Node::set_parameters_if_not_set(
const std::string & name,
const std::map<std::string, MapValueT> & values)
{
std::vector<rclcpp::Parameter> params;
for (const auto & val : values) {
std::string param_name = name + "." + val.first;
rclcpp::Parameter parameter;
if (!this->get_parameter(param_name, parameter)) {
params.push_back(rclcpp::Parameter(param_name, val.second));
}
}
this->set_parameters(params);
}
template<typename ParameterT>
bool
Node::get_parameter(const std::string & name, ParameterT & value) const
@@ -237,6 +259,28 @@ Node::get_parameter(const std::string & name, ParameterT & value) const
return result;
}
// this is a partially-specialized version of get_parameter above,
// where our concrete type for ParameterT is std::map, but the to-be-determined
// type is the value in the map.
template<typename MapValueT>
bool
Node::get_parameters(
const std::string & name,
std::map<std::string, MapValueT> & values) const
{
bool retval = false;
std::vector<std::string> prefix{name};
std::string name_with_dot = name + ".";
rcl_interfaces::msg::ListParametersResult result = node_parameters_->list_parameters(prefix, 0);
for (const auto & param : result.names) {
values[param.substr(name_with_dot.length())] =
node_parameters_->get_parameter(param).get_value<MapValueT>();
retval = true;
}
return retval;
}
template<typename ParameterT>
bool
Node::get_parameter_or(

View File

@@ -38,6 +38,10 @@ class NodeBaseInterface
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeBaseInterface)
RCLCPP_PUBLIC
virtual
~NodeBaseInterface() = default;
/// Return the name of the node.
/** \return The name of the node. */
RCLCPP_PUBLIC

View File

@@ -64,7 +64,6 @@ private:
rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging_;
rclcpp::Clock::SharedPtr ros_clock_;
rclcpp::TimeSource time_source_;
};
} // namespace node_interfaces

View File

@@ -31,6 +31,10 @@ class NodeClockInterface
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeClockInterface)
RCLCPP_PUBLIC
virtual
~NodeClockInterface() = default;
/// Get a ROS clock which will be kept up to date by the node.
RCLCPP_PUBLIC
virtual

View File

@@ -38,6 +38,10 @@ class NodeGraphInterface
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeGraphInterface)
RCLCPP_PUBLIC
virtual
~NodeGraphInterface() = default;
/// Return a map of existing topic names to list of topic types.
/**
* A topic is considered to exist when at least one publisher or subscriber

View File

@@ -33,6 +33,10 @@ class NodeLoggingInterface
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeLoggingInterface)
RCLCPP_PUBLIC
virtual
~NodeLoggingInterface() = default;
/// Return the logger of the node.
/** \return The logger of the node. */
RCLCPP_PUBLIC

View File

@@ -37,6 +37,10 @@ class NodeParametersInterface
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeParametersInterface)
RCLCPP_PUBLIC
virtual
~NodeParametersInterface() = default;
RCLCPP_PUBLIC
virtual
std::vector<rcl_interfaces::msg::SetParametersResult>

View File

@@ -32,6 +32,10 @@ class NodeServicesInterface
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeServicesInterface)
RCLCPP_PUBLIC
virtual
~NodeServicesInterface() = default;
RCLCPP_PUBLIC
virtual
void

View File

@@ -0,0 +1,72 @@
// Copyright 2018 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__NODE_INTERFACES__NODE_TIME_SOURCE_HPP_
#define RCLCPP__NODE_INTERFACES__NODE_TIME_SOURCE_HPP_
#include "rclcpp/callback_group.hpp"
#include "rclcpp/clock.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/node_interfaces/node_clock_interface.hpp"
#include "rclcpp/node_interfaces/node_parameters_interface.hpp"
#include "rclcpp/node_interfaces/node_time_source_interface.hpp"
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
#include "rclcpp/time_source.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace node_interfaces
{
/// Implementation of the NodeTimeSource part of the Node API.
class NodeTimeSource : public NodeTimeSourceInterface
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeTimeSource)
RCLCPP_PUBLIC
explicit NodeTimeSource(
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base,
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics,
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph,
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services,
rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging,
rclcpp::node_interfaces::NodeClockInterface::SharedPtr node_clock,
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters
);
RCLCPP_PUBLIC
virtual
~NodeTimeSource();
private:
RCLCPP_DISABLE_COPY(NodeTimeSource)
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base_;
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics_;
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph_;
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services_;
rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging_;
rclcpp::node_interfaces::NodeClockInterface::SharedPtr node_clock_;
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters_;
rclcpp::TimeSource time_source_;
};
} // namespace node_interfaces
} // namespace rclcpp
#endif // RCLCPP__NODE_INTERFACES__NODE_TIME_SOURCE_HPP_

View File

@@ -0,0 +1,42 @@
// Copyright 2018 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__NODE_INTERFACES__NODE_TIME_SOURCE_INTERFACE_HPP_
#define RCLCPP__NODE_INTERFACES__NODE_TIME_SOURCE_INTERFACE_HPP_
#include "rclcpp/callback_group.hpp"
#include "rclcpp/clock.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace node_interfaces
{
/// Pure virtual interface class for the NodeTimeSource part of the Node API.
class NodeTimeSourceInterface
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeTimeSourceInterface)
RCLCPP_PUBLIC
virtual
~NodeTimeSourceInterface() = default;
};
} // namespace node_interfaces
} // namespace rclcpp
#endif // RCLCPP__NODE_INTERFACES__NODE_TIME_SOURCE_INTERFACE_HPP_

View File

@@ -31,6 +31,10 @@ class NodeTimersInterface
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeTimersInterface)
RCLCPP_PUBLIC
virtual
~NodeTimersInterface() = default;
/// Add a timer to the node.
RCLCPP_PUBLIC
virtual

View File

@@ -40,6 +40,10 @@ class NodeTopicsInterface
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeTopicsInterface)
RCLCPP_PUBLIC
virtual
~NodeTopicsInterface() = default;
RCLCPP_PUBLIC
virtual
rclcpp::PublisherBase::SharedPtr

View File

@@ -31,6 +31,10 @@ class NodeWaitablesInterface
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeWaitablesInterface)
RCLCPP_PUBLIC
virtual
~NodeWaitablesInterface() = default;
RCLCPP_PUBLIC
virtual
void

View File

@@ -51,7 +51,9 @@ public:
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics_interface,
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph_interface,
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services_interface,
rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging_interface);
rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging_interface,
rclcpp::node_interfaces::NodeClockInterface::SharedPtr node_clock_interface,
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters_interface);
RCLCPP_PUBLIC
void detachNode();
@@ -76,6 +78,8 @@ private:
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph_;
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services_;
rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging_;
rclcpp::node_interfaces::NodeClockInterface::SharedPtr node_clock_;
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters_;
// Store (and update on node attach) logger for logging.
Logger logger_;

View File

@@ -24,9 +24,6 @@
#include "rclcpp/init_options.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rcl/guard_condition.h"
#include "rcl/wait.h"
#include "rmw/macros.h"
#include "rmw/rmw.h"
@@ -184,9 +181,11 @@ shutdown(
* If nullptr is given for the context, then the global context is used, i.e.
* the context initialized by rclcpp::init().
*
* Note that these callbacks should be non-blocking and not throw exceptions,
* as they may be called from signal handlers and from the destructor of the
* context.
* These callbacks are called when the associated Context is shutdown with the
* Context::shutdown() method.
* When shutdown by the SIGINT handler, shutdown, and therefore these callbacks,
* is called asynchronously from the dedicated signal handling thread, at some
* point after the SIGINT signal is received.
*
* \sa rclcpp::Context::on_shutdown()
* \param[in] callback to be called when the given context is shutdown
@@ -196,53 +195,18 @@ RCLCPP_PUBLIC
void
on_shutdown(std::function<void()> callback, rclcpp::Context::SharedPtr context = nullptr);
/// Get a handle to the rmw guard condition that manages the signal handler.
/**
* The first time that this function is called for a given wait set a new guard
* condition will be created and returned; thereafter the same guard condition
* will be returned for the same wait set. This mechanism is designed to ensure
* that the same guard condition is not reused across wait sets (e.g., when
* using multiple executors in the same process). Will throw an exception if
* initialization of the guard condition fails.
*
* \param[in] wait_set Pointer to the rcl_wait_set_t that will be using the
* resulting guard condition.
* \param[in] context The context associated with the guard condition.
* \return Pointer to the guard condition.
*/
RCLCPP_PUBLIC
rcl_guard_condition_t *
get_sigint_guard_condition(rcl_wait_set_t * wait_set, rclcpp::Context::SharedPtr context);
/// Release the previously allocated guard condition that manages the signal handler.
/**
* If you previously called get_sigint_guard_condition() for a given wait set
* to get a sigint guard condition, then you should call
* release_sigint_guard_condition() when you're done, to free that condition.
* Will throw an exception if get_sigint_guard_condition() wasn't previously
* called for the given wait set.
*
* If nullptr is given for the context, then the global context is used, i.e.
* the context initialized by rclcpp::init().
*
* \param[in] wait_set Pointer to the rcl_wait_set_t that was using the
* resulting guard condition.
*/
RCLCPP_PUBLIC
void
release_sigint_guard_condition(rcl_wait_set_t * wait_set);
/// Use the global condition variable to block for the specified amount of time.
/**
* This function can be interrupted early if the associated context becomes
* invalid due to rclcpp::shutdown() or the signal handler.
* invalid due to shutdown() or the signal handler.
* \sa rclcpp::Context::sleep_for
*
* If nullptr is given for the context, then the global context is used, i.e.
* the context initialized by rclcpp::init().
*
* \param[in] nanoseconds A std::chrono::duration representing how long to sleep for.
* \param[in] context which may interrupt this sleep
* \return True if the condition variable did not timeout.
* \return true if the condition variable did not timeout.
*/
RCLCPP_PUBLIC
bool
@@ -250,11 +214,6 @@ sleep_for(
const std::chrono::nanoseconds & nanoseconds,
rclcpp::Context::SharedPtr context = nullptr);
/// Notify all blocking calls so they can check for changes in rclcpp::ok().
RCLCPP_PUBLIC
void
notify_all();
/// Safely check if addition will overflow.
/**
* The type of the operands, T, should have defined

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>0.6.1</version>
<version>0.6.5</version>
<description>The ROS client library in C++.</description>
<maintainer email="dthomas@osrfoundation.org">Dirk Thomas</maintainer>
<license>Apache License 2.0</license>

View File

@@ -22,6 +22,7 @@
#include "rcl/init.h"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/logging.hpp"
#include "rmw/impl/cpp/demangle.hpp"
/// Mutex to protect initialized contexts.
static std::mutex g_contexts_mutex;
@@ -127,7 +128,7 @@ Context::shutdown_reason()
}
bool
Context::shutdown(const std::string & reason, bool notify_all)
Context::shutdown(const std::string & reason)
{
// prevent races
std::lock_guard<std::recursive_mutex> init_lock(init_mutex_);
@@ -147,10 +148,9 @@ Context::shutdown(const std::string & reason, bool notify_all)
for (const auto & callback : on_shutdown_callbacks_) {
callback();
}
// notify all blocking calls, if asked
if (notify_all) {
rclcpp::notify_all();
}
// interrupt all blocking sleep_for() and all blocking executors or wait sets
this->interrupt_all_sleep_for();
this->interrupt_all_wait_sets();
// remove self from the global contexts
std::lock_guard<std::mutex> context_lock(g_contexts_mutex);
for (auto it = g_contexts.begin(); it != g_contexts.end(); ) {
@@ -190,6 +190,99 @@ Context::get_rcl_context()
return rcl_context_;
}
bool
Context::sleep_for(const std::chrono::nanoseconds & nanoseconds)
{
std::chrono::nanoseconds time_left = nanoseconds;
{
std::unique_lock<std::mutex> lock(interrupt_mutex_);
auto start = std::chrono::steady_clock::now();
// this will release the lock while waiting
interrupt_condition_variable_.wait_for(lock, nanoseconds);
time_left -= std::chrono::steady_clock::now() - start;
}
if (time_left > std::chrono::nanoseconds::zero() && this->is_valid()) {
return sleep_for(time_left);
}
// Return true if the timeout elapsed successfully, otherwise false.
return this->is_valid();
}
void
Context::interrupt_all_sleep_for()
{
interrupt_condition_variable_.notify_all();
}
rcl_guard_condition_t *
Context::get_interrupt_guard_condition(rcl_wait_set_t * wait_set)
{
std::lock_guard<std::mutex> lock(interrupt_guard_cond_handles_mutex_);
auto kv = interrupt_guard_cond_handles_.find(wait_set);
if (kv != interrupt_guard_cond_handles_.end()) {
return &kv->second;
} else {
rcl_guard_condition_t handle = rcl_get_zero_initialized_guard_condition();
rcl_guard_condition_options_t options = rcl_guard_condition_get_default_options();
auto ret = rcl_guard_condition_init(&handle, this->get_rcl_context().get(), options);
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "Couldn't initialize guard condition");
}
interrupt_guard_cond_handles_.emplace(wait_set, handle);
return &interrupt_guard_cond_handles_[wait_set];
}
}
void
Context::release_interrupt_guard_condition(rcl_wait_set_t * wait_set)
{
std::lock_guard<std::mutex> lock(interrupt_guard_cond_handles_mutex_);
auto kv = interrupt_guard_cond_handles_.find(wait_set);
if (kv != interrupt_guard_cond_handles_.end()) {
rcl_ret_t ret = rcl_guard_condition_fini(&kv->second);
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "Failed to destroy sigint guard condition");
}
interrupt_guard_cond_handles_.erase(kv);
} else {
throw std::runtime_error("Tried to release sigint guard condition for nonexistent wait set");
}
}
void
Context::release_interrupt_guard_condition(
rcl_wait_set_t * wait_set,
const std::nothrow_t &) noexcept
{
try {
this->release_interrupt_guard_condition(wait_set);
} catch (const std::exception & exc) {
RCLCPP_ERROR(
rclcpp::get_logger("rclcpp"),
"caught %s exception when releasing interrupt guard condition: %s",
rmw::impl::cpp::demangle(exc).c_str(), exc.what());
} catch (...) {
RCLCPP_ERROR(
rclcpp::get_logger("rclcpp"),
"caught unknown exception when releasing interrupt guard condition");
}
}
void
Context::interrupt_all_wait_sets()
{
std::lock_guard<std::mutex> lock(interrupt_guard_cond_handles_mutex_);
for (auto & kv : interrupt_guard_cond_handles_) {
rcl_ret_t status = rcl_trigger_guard_condition(&(kv.second));
if (status != RCL_RET_OK) {
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"failed to trigger guard condition in Context::interrupt_all_wait_sets(): %s",
rcl_get_error_string().str);
}
}
}
void
Context::clean_up()
{

View File

@@ -51,8 +51,7 @@ Executor::Executor(const ExecutorArgs & args)
// and one for the executor's guard cond (interrupt_guard_condition_)
// Put the global ctrl-c guard condition in
memory_strategy_->add_guard_condition(
rclcpp::get_sigint_guard_condition(&wait_set_, args.context));
memory_strategy_->add_guard_condition(args.context->get_interrupt_guard_condition(&wait_set_));
// Put the executor's guard condition in
memory_strategy_->add_guard_condition(&interrupt_guard_condition_);
@@ -105,9 +104,8 @@ Executor::~Executor()
rcl_reset_error();
}
// Remove and release the sigint guard condition
memory_strategy_->remove_guard_condition(
rclcpp::get_sigint_guard_condition(&wait_set_, context_));
rclcpp::release_sigint_guard_condition(&wait_set_);
memory_strategy_->remove_guard_condition(context_->get_interrupt_guard_condition(&wait_set_));
context_->release_interrupt_guard_condition(&wait_set_, std::nothrow);
}
void

View File

@@ -36,7 +36,8 @@ namespace graph_listener
{
GraphListener::GraphListener(std::shared_ptr<rclcpp::Context> parent_context)
: is_started_(false),
: parent_context_(parent_context),
is_started_(false),
is_shutdown_(false),
interrupt_guard_condition_context_(nullptr),
shutdown_guard_condition_(nullptr)
@@ -54,12 +55,12 @@ GraphListener::GraphListener(std::shared_ptr<rclcpp::Context> parent_context)
throw_from_rcl_error(ret, "failed to create interrupt guard condition");
}
shutdown_guard_condition_ = rclcpp::get_sigint_guard_condition(&wait_set_, parent_context);
shutdown_guard_condition_ = parent_context->get_interrupt_guard_condition(&wait_set_);
}
GraphListener::~GraphListener()
{
this->shutdown();
this->shutdown(std::nothrow);
}
void
@@ -90,7 +91,8 @@ GraphListener::start_if_not_started()
[weak_this]() {
auto shared_this = weak_this.lock();
if (shared_this) {
shared_this->shutdown();
// should not throw from on_shutdown if it can be avoided
shared_this->shutdown(std::nothrow);
}
});
// Start the listener thread.
@@ -337,7 +339,7 @@ GraphListener::remove_node(rclcpp::node_interfaces::NodeGraphInterface * node_gr
}
void
GraphListener::shutdown()
GraphListener::__shutdown(bool should_throw)
{
std::lock_guard<std::mutex> shutdown_lock(shutdown_mutex_);
if (!is_shutdown_.exchange(true)) {
@@ -351,7 +353,11 @@ GraphListener::shutdown()
throw_from_rcl_error(ret, "failed to finalize interrupt guard condition");
}
if (shutdown_guard_condition_) {
rclcpp::release_sigint_guard_condition(&wait_set_);
if (should_throw) {
parent_context_->release_interrupt_guard_condition(&wait_set_);
} else {
parent_context_->release_interrupt_guard_condition(&wait_set_, std::nothrow);
}
shutdown_guard_condition_ = nullptr;
}
if (is_started_) {
@@ -363,6 +369,29 @@ GraphListener::shutdown()
}
}
void
GraphListener::shutdown()
{
this->__shutdown(true);
}
void
GraphListener::shutdown(const std::nothrow_t &) noexcept
{
try {
this->__shutdown(false);
} catch (const std::exception & exc) {
RCLCPP_ERROR(
rclcpp::get_logger("rclcpp"),
"caught %s exception when shutting down GraphListener: %s",
rmw::impl::cpp::demangle(exc).c_str(), exc.what());
} catch (...) {
RCLCPP_ERROR(
rclcpp::get_logger("rclcpp"),
"caught unknown exception when shutting down GraphListener");
}
}
bool
GraphListener::is_shutdown()
{

View File

@@ -29,6 +29,7 @@
#include "rclcpp/node_interfaces/node_logging.hpp"
#include "rclcpp/node_interfaces/node_parameters.hpp"
#include "rclcpp/node_interfaces/node_services.hpp"
#include "rclcpp/node_interfaces/node_time_source.hpp"
#include "rclcpp/node_interfaces/node_timers.hpp"
#include "rclcpp/node_interfaces/node_topics.hpp"
#include "rclcpp/node_interfaces/node_waitables.hpp"
@@ -83,6 +84,15 @@ Node::Node(
use_intra_process_comms,
start_parameter_services
)),
node_time_source_(new rclcpp::node_interfaces::NodeTimeSource(
node_base_,
node_topics_,
node_graph_,
node_services_,
node_logging_,
node_clock_,
node_parameters_
)),
node_waitables_(new rclcpp::node_interfaces::NodeWaitables(node_base_.get())),
use_intra_process_comms_(use_intra_process_comms)
{
@@ -263,6 +273,12 @@ Node::get_node_logging_interface()
return node_logging_;
}
rclcpp::node_interfaces::NodeTimeSourceInterface::SharedPtr
Node::get_node_time_source_interface()
{
return node_time_source_;
}
rclcpp::node_interfaces::NodeTimersInterface::SharedPtr
Node::get_node_timers_interface()
{

View File

@@ -31,15 +31,7 @@ NodeClock::NodeClock(
node_services_(node_services),
node_logging_(node_logging),
ros_clock_(std::make_shared<rclcpp::Clock>(RCL_ROS_TIME))
{
time_source_.attachNode(
node_base_,
node_topics_,
node_graph_,
node_services_,
node_logging_);
time_source_.attachClock(ros_clock_);
}
{}
NodeClock::~NodeClock()
{}

View File

@@ -0,0 +1,50 @@
// Copyright 2018 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "rclcpp/node_interfaces/node_time_source.hpp"
#include <memory>
#include <string>
using rclcpp::node_interfaces::NodeTimeSource;
NodeTimeSource::NodeTimeSource(
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base,
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics,
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph,
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services,
rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging,
rclcpp::node_interfaces::NodeClockInterface::SharedPtr node_clock,
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters)
: node_base_(node_base),
node_topics_(node_topics),
node_graph_(node_graph),
node_services_(node_services),
node_logging_(node_logging),
node_clock_(node_clock),
node_parameters_(node_parameters)
{
time_source_.attachNode(
node_base_,
node_topics_,
node_graph_,
node_services_,
node_logging_,
node_clock_,
node_parameters_);
time_source_.attachClock(node_clock_->get_clock());
}
NodeTimeSource::~NodeTimeSource()
{}

View File

@@ -0,0 +1,357 @@
// Copyright 2018 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 "./signal_handler.hpp"
#include <atomic>
#include <csignal>
#include <mutex>
#include <string>
#include <thread>
// includes for semaphore notification code
#if defined(_WIN32)
#include <windows.h>
#elif defined(__APPLE__)
#include <dispatch/dispatch.h>
#else // posix
#include <semaphore.h>
#endif
#include "rclcpp/logging.hpp"
#include "rmw/impl/cpp/demangle.hpp"
using rclcpp::SignalHandler;
// initialize static storage in SignalHandler class
SignalHandler::signal_handler_type SignalHandler::old_signal_handler_;
std::atomic_bool SignalHandler::signal_received_ = ATOMIC_VAR_INIT(false);
std::atomic_bool SignalHandler::wait_for_signal_is_setup_ = ATOMIC_VAR_INIT(false);
#if defined(_WIN32)
HANDLE SignalHandler::signal_handler_sem_;
#elif defined(__APPLE__)
dispatch_semaphore_t SignalHandler::signal_handler_sem_;
#else // posix
sem_t SignalHandler::signal_handler_sem_;
#endif
SignalHandler &
SignalHandler::get_global_signal_handler()
{
static SignalHandler signal_handler;
return signal_handler;
}
rclcpp::Logger &
SignalHandler::get_logger()
{
static rclcpp::Logger logger = rclcpp::get_logger("rclcpp");
return logger;
}
bool
SignalHandler::install()
{
std::lock_guard<std::mutex> lock(install_mutex_);
bool already_installed = installed_.exchange(true);
if (already_installed) {
return false;
}
try {
setup_wait_for_signal();
signal_received_.store(false);
SignalHandler::signal_handler_type signal_handler_argument;
#if defined(RCLCPP_HAS_SIGACTION)
memset(&signal_handler_argument, 0, sizeof(signal_handler_argument));
sigemptyset(&signal_handler_argument.sa_mask);
signal_handler_argument.sa_sigaction = signal_handler;
signal_handler_argument.sa_flags = SA_SIGINFO;
#else
signal_handler_argument = signal_handler;
#endif
old_signal_handler_ = SignalHandler::set_signal_handler(SIGINT, signal_handler_argument);
signal_handler_thread_ = std::thread(&SignalHandler::deferred_signal_handler, this);
} catch (...) {
installed_.store(false);
throw;
}
RCLCPP_DEBUG(get_logger(), "signal handler installed");
return true;
}
bool
SignalHandler::uninstall()
{
std::lock_guard<std::mutex> lock(install_mutex_);
bool installed = installed_.exchange(false);
if (!installed) {
return false;
}
try {
// TODO(wjwwood): what happens if someone overrides our signal handler then calls uninstall?
// I think we need to assert that we're the current signal handler, and mitigate if not.
set_signal_handler(SIGINT, old_signal_handler_);
RCLCPP_DEBUG(get_logger(), "SignalHandler::uninstall(): notifying deferred signal handler");
notify_signal_handler();
signal_handler_thread_.join();
teardown_wait_for_signal();
} catch (...) {
installed_.exchange(true);
throw;
}
RCLCPP_DEBUG(get_logger(), "signal handler uninstalled");
return true;
}
bool
SignalHandler::is_installed()
{
return installed_.load();
}
SignalHandler::~SignalHandler()
{
try {
uninstall();
} catch (const std::exception & exc) {
RCLCPP_ERROR(
get_logger(),
"caught %s exception when uninstalling the sigint handler in rclcpp::~SignalHandler: %s",
rmw::impl::cpp::demangle(exc).c_str(), exc.what());
} catch (...) {
RCLCPP_ERROR(
get_logger(),
"caught unknown exception when uninstalling the sigint handler in rclcpp::~SignalHandler");
}
}
RCLCPP_LOCAL
void
__safe_strerror(int errnum, char * buffer, size_t buffer_length)
{
#if defined(_WIN32)
strerror_s(buffer, buffer_length, errnum);
#elif (defined(_GNU_SOURCE) && !defined(ANDROID))
char * msg = strerror_r(errnum, buffer, buffer_length);
if (msg != buffer) {
strncpy(buffer, msg, buffer_length);
buffer[buffer_length - 1] = '\0';
}
#else
int error_status = strerror_r(errnum, buffer, buffer_length);
if (error_status != 0) {
throw std::runtime_error("Failed to get error string for errno: " + std::to_string(errnum));
}
#endif
}
SignalHandler::signal_handler_type
SignalHandler::set_signal_handler(
int signal_value,
const SignalHandler::signal_handler_type & signal_handler)
{
bool signal_handler_install_failed;
SignalHandler::signal_handler_type old_signal_handler;
#if defined(RCLCPP_HAS_SIGACTION)
ssize_t ret = sigaction(signal_value, &signal_handler, &old_signal_handler);
signal_handler_install_failed = (ret == -1);
#else
old_signal_handler = std::signal(signal_value, signal_handler);
signal_handler_install_failed = (old_signal_handler == SIG_ERR);
#endif
if (signal_handler_install_failed) {
char error_string[1024];
__safe_strerror(errno, error_string, sizeof(error_string));
auto msg =
"Failed to set SIGINT signal handler (" + std::to_string(errno) + "): " + error_string;
throw std::runtime_error(msg);
}
return old_signal_handler;
}
void
SignalHandler::signal_handler_common()
{
signal_received_.store(true);
RCLCPP_DEBUG(
get_logger(),
"signal_handler(): SIGINT received, notifying deferred signal handler");
notify_signal_handler();
}
#if defined(RCLCPP_HAS_SIGACTION)
void
SignalHandler::signal_handler(int signal_value, siginfo_t * siginfo, void * context)
{
RCLCPP_INFO(get_logger(), "signal_handler(signal_value=%d)", signal_value);
if (old_signal_handler_.sa_flags & SA_SIGINFO) {
if (old_signal_handler_.sa_sigaction != NULL) {
old_signal_handler_.sa_sigaction(signal_value, siginfo, context);
}
} else {
if (
old_signal_handler_.sa_handler != NULL && // Is set
old_signal_handler_.sa_handler != SIG_DFL && // Is not default
old_signal_handler_.sa_handler != SIG_IGN) // Is not ignored
{
old_signal_handler_.sa_handler(signal_value);
}
}
signal_handler_common();
}
#else
void
SignalHandler::signal_handler(int signal_value)
{
RCLCPP_INFO(get_logger(), "signal_handler(signal_value=%d)", signal_value);
if (old_signal_handler_) {
old_signal_handler_(signal_value);
}
signal_handler_common();
}
#endif
void
SignalHandler::deferred_signal_handler()
{
while (true) {
if (signal_received_.exchange(false)) {
RCLCPP_DEBUG(get_logger(), "deferred_signal_handler(): SIGINT received, shutting down");
for (auto context_ptr : rclcpp::get_contexts()) {
if (context_ptr->get_init_options().shutdown_on_sigint) {
RCLCPP_DEBUG(
get_logger(),
"deferred_signal_handler(): "
"shutting down rclcpp::Context @ %p, because it had shutdown_on_sigint == true",
context_ptr.get());
context_ptr->shutdown("signal handler");
}
}
}
if (!is_installed()) {
RCLCPP_DEBUG(get_logger(), "deferred_signal_handler(): signal handling uninstalled");
break;
}
RCLCPP_DEBUG(get_logger(), "deferred_signal_handler(): waiting for SIGINT or uninstall");
wait_for_signal();
RCLCPP_DEBUG(get_logger(), "deferred_signal_handler(): woken up due to SIGINT or uninstall");
}
}
void
SignalHandler::setup_wait_for_signal()
{
#if defined(_WIN32)
signal_handler_sem_ = CreateSemaphore(
NULL, // default security attributes
0, // initial semaphore count
1, // maximum semaphore count
NULL); // unnamed semaphore
if (NULL == signal_handler_sem_) {
throw std::runtime_error("CreateSemaphore() failed in setup_wait_for_signal()");
}
#elif defined(__APPLE__)
signal_handler_sem_ = dispatch_semaphore_create(0);
#else // posix
if (-1 == sem_init(&signal_handler_sem_, 0, 0)) {
throw std::runtime_error(std::string("sem_init() failed: ") + strerror(errno));
}
#endif
wait_for_signal_is_setup_.store(true);
}
void
SignalHandler::teardown_wait_for_signal() noexcept
{
if (!wait_for_signal_is_setup_.exchange(false)) {
return;
}
#if defined(_WIN32)
CloseHandle(signal_handler_sem_);
#elif defined(__APPLE__)
dispatch_release(signal_handler_sem_);
#else // posix
if (-1 == sem_destroy(&signal_handler_sem_)) {
RCLCPP_ERROR(get_logger(), "invalid semaphore in teardown_wait_for_signal()");
}
#endif
}
void
SignalHandler::wait_for_signal()
{
if (!wait_for_signal_is_setup_.load()) {
RCLCPP_ERROR(get_logger(), "called wait_for_signal() before setup_wait_for_signal()");
return;
}
#if defined(_WIN32)
DWORD dw_wait_result = WaitForSingleObject(signal_handler_sem_, INFINITE);
switch (dw_wait_result) {
case WAIT_ABANDONED:
RCLCPP_ERROR(
get_logger(), "WaitForSingleObject() failed in wait_for_signal() with WAIT_ABANDONED: %s",
GetLastError());
break;
case WAIT_OBJECT_0:
// successful
break;
case WAIT_TIMEOUT:
RCLCPP_ERROR(get_logger(), "WaitForSingleObject() timedout out in wait_for_signal()");
break;
case WAIT_FAILED:
RCLCPP_ERROR(
get_logger(), "WaitForSingleObject() failed in wait_for_signal(): %s", GetLastError());
break;
default:
RCLCPP_ERROR(
get_logger(), "WaitForSingleObject() gave unknown return in wait_for_signal(): %s",
GetLastError());
}
#elif defined(__APPLE__)
dispatch_semaphore_wait(signal_handler_sem_, DISPATCH_TIME_FOREVER);
#else // posix
int s;
do {
s = sem_wait(&signal_handler_sem_);
} while (-1 == s && EINTR == errno);
#endif
}
void
SignalHandler::notify_signal_handler() noexcept
{
if (!wait_for_signal_is_setup_.load()) {
return;
}
#if defined(_WIN32)
if (!ReleaseSemaphore(signal_handler_sem_, 1, NULL)) {
RCLCPP_ERROR(
get_logger(), "ReleaseSemaphore() failed in notify_signal_handler(): %s", GetLastError());
}
#elif defined(__APPLE__)
dispatch_semaphore_signal(signal_handler_sem_);
#else // posix
if (-1 == sem_post(&signal_handler_sem_)) {
RCLCPP_ERROR(get_logger(), "sem_post failed in notify_signal_handler()");
}
#endif
}

View File

@@ -0,0 +1,189 @@
// Copyright 2018 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__SIGNAL_HANDLER_HPP_
#define RCLCPP__SIGNAL_HANDLER_HPP_
#include <atomic>
#include <csignal>
#include <mutex>
#include <thread>
#include "rclcpp/logging.hpp"
// includes for semaphore notification code
#if defined(_WIN32)
#include <windows.h>
#elif defined(__APPLE__)
#include <dispatch/dispatch.h>
#else // posix
#include <semaphore.h>
#endif
// Determine if sigaction is available
#if __APPLE__ || _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE
#define RCLCPP_HAS_SIGACTION
#endif
namespace rclcpp
{
/// Responsible for manaaging the SIGINT signal handling.
/**
* This class is responsible for:
*
* - installing the signal handler for SIGINT
* - uninstalling the signal handler for SIGINT
* - creating a thread to execute "on sigint" work outside of the signal handler
* - safely notifying the dedicated signal handling thread when receiving SIGINT
* - implementation of all of the signal handling work, like shutting down contexts
*
* \internal
*/
class SignalHandler final
{
public:
/// Return the global singleton of this class.
static
SignalHandler &
get_global_signal_handler();
/// Return a global singleton logger to avoid needing to create it everywhere.
static
rclcpp::Logger &
get_logger();
/// Install the signal handler for SIGINT and start the dedicated signal handling thread.
/**
* Also stores the current signal handler to be called on SIGINT and to
* restore when uninstalling this signal handler.
*/
bool
install();
/// Uninstall the signal handler for SIGINT and join the dedicated singal handling thread.
/**
* Also restores the previous signal handler.
*/
bool
uninstall();
/// Return true if installed, false otherwise.
bool
is_installed();
private:
SignalHandler() = default;
~SignalHandler();
#if defined(RCLCPP_HAS_SIGACTION)
using signal_handler_type = struct sigaction;
#else
using signal_handler_type = void (*)(int);
#endif
// POSIX signal handler structure storage for the existing signal handler.
static SignalHandler::signal_handler_type old_signal_handler_;
/// Set the signal handler function.
static
SignalHandler::signal_handler_type
set_signal_handler(int signal_value, const SignalHandler::signal_handler_type & signal_handler);
/// Common signal handler code between sigaction and non-sigaction versions.
static
void
signal_handler_common();
#if defined(RCLCPP_HAS_SIGACTION)
/// Signal handler function.
static
void
signal_handler(int signal_value, siginfo_t * siginfo, void * context);
#else
/// Signal handler function.
static
void
signal_handler(int signal_value);
#endif
/// Target of the dedicated signal handling thread.
void
deferred_signal_handler();
/// Setup anything that is necessary for wait_for_signal() or notify_signal_handler().
/**
* This must be called before wait_for_signal() or notify_signal_handler().
* This is not thread-safe.
*/
static
void
setup_wait_for_signal();
/// Undo all setup done in setup_wait_for_signal().
/**
* Must not call wait_for_signal() or notify_signal_handler() after calling this.
*
* This is not thread-safe.
*/
static
void
teardown_wait_for_signal() noexcept;
/// Wait for a notification from notify_signal_handler() in a signal safe way.
/**
* This static method may throw if posting the semaphore fails.
*
* This is not thread-safe.
*/
static
void
wait_for_signal();
/// Notify blocking wait_for_signal() calls in a signal safe way.
/**
* This is used to notify the deferred_signal_handler() thread to start work
* from the signal handler.
*
* This is thread-safe.
*/
static
void
notify_signal_handler() noexcept;
// Whether or not a signal has been received.
static std::atomic_bool signal_received_;
// A thread to which singal handling tasks are deferred.
std::thread signal_handler_thread_;
// A mutex used to synchronize the install() and uninstall() methods.
std::mutex install_mutex_;
// Whether or not the signal handler has been installed.
std::atomic_bool installed_{false};
// Whether or not the semaphore for wait_for_signal is setup.
static std::atomic_bool wait_for_signal_is_setup_;
// Storage for the wait_for_signal semaphore.
#if defined(_WIN32)
static HANDLE signal_handler_sem_;
#elif defined(__APPLE__)
static dispatch_semaphore_t signal_handler_sem_;
#else // posix
static sem_t signal_handler_sem_;
#endif
};
} // namespace rclcpp
#endif // RCLCPP__SIGNAL_HANDLER_HPP_

View File

@@ -53,7 +53,9 @@ void TimeSource::attachNode(rclcpp::Node::SharedPtr node)
node->get_node_topics_interface(),
node->get_node_graph_interface(),
node->get_node_services_interface(),
node->get_node_logging_interface());
node->get_node_logging_interface(),
node->get_node_clock_interface(),
node->get_node_parameters_interface());
}
void TimeSource::attachNode(
@@ -61,17 +63,38 @@ void TimeSource::attachNode(
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics_interface,
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph_interface,
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services_interface,
rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging_interface)
rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging_interface,
rclcpp::node_interfaces::NodeClockInterface::SharedPtr node_clock_interface,
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters_interface)
{
node_base_ = node_base_interface;
node_topics_ = node_topics_interface;
node_graph_ = node_graph_interface;
node_services_ = node_services_interface;
node_logging_ = node_logging_interface;
node_clock_ = node_clock_interface;
node_parameters_ = node_parameters_interface;
// TODO(tfoote): Update QOS
logger_ = node_logging_->get_logger();
rclcpp::Parameter use_sim_time_param;
if (node_parameters_->get_parameter("use_sim_time", use_sim_time_param)) {
if (use_sim_time_param.get_type() == rclcpp::PARAMETER_BOOL) {
if (use_sim_time_param.get_value<bool>() == true) {
parameter_state_ = SET_TRUE;
enable_ros_time();
create_clock_sub();
}
} else {
RCLCPP_ERROR(logger_, "Invalid type for parameter 'use_sim_time' %s should be bool",
use_sim_time_param.get_type_name().c_str());
}
} else {
RCLCPP_DEBUG(logger_, "'use_sim_time' parameter not set, using wall time by default.");
}
// TODO(tfoote) use parameters interface not subscribe to events via topic ticketed #609
parameter_client_ = std::make_shared<rclcpp::AsyncParametersClient>(
node_base_,
node_topics_,
@@ -92,6 +115,9 @@ void TimeSource::detachNode()
node_topics_.reset();
node_graph_.reset();
node_services_.reset();
node_logging_.reset();
node_clock_.reset();
node_parameters_.reset();
disable_ros_time();
}
@@ -103,11 +129,12 @@ void TimeSource::attachClock(std::shared_ptr<rclcpp::Clock> clock)
std::lock_guard<std::mutex> guard(clock_list_lock_);
associated_clocks_.push_back(clock);
// Set the clock if there's already data for it
// 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_) {
auto time_msg = std::make_shared<builtin_interfaces::msg::Time>(last_msg_set_->clock);
set_clock(time_msg, ros_time_active_, clock);
time_msg = std::make_shared<builtin_interfaces::msg::Time>(last_msg_set_->clock);
}
set_clock(time_msg, ros_time_active_, clock);
}
void TimeSource::detachClock(std::shared_ptr<rclcpp::Clock> clock)
@@ -123,7 +150,9 @@ void TimeSource::detachClock(std::shared_ptr<rclcpp::Clock> clock)
TimeSource::~TimeSource()
{
if (node_base_ || node_topics_ || node_graph_ || node_services_) {
if (node_base_ || node_topics_ || node_graph_ || node_services_ ||
node_logging_ || node_clock_ || node_parameters_)
{
this->detachNode();
}
}
@@ -257,13 +286,14 @@ void TimeSource::enable_ros_time()
// Local storage
ros_time_active_ = true;
// Update all attached clocks
// 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) {
auto msg = std::make_shared<builtin_interfaces::msg::Time>();
msg->sec = 0;
msg->nanosec = 0;
set_clock(msg, true, *it);
set_clock(time_msg, true, *it);
}
}

View File

@@ -14,145 +14,16 @@
#include "rclcpp/utilities.hpp"
#include <atomic>
#include <condition_variable>
#include <csignal>
#include <cstdio>
#include <cstring>
#include <map>
#include <mutex>
#include <string>
#include <vector>
#include "./signal_handler.hpp"
#include "rclcpp/contexts/default_context.hpp"
#include "rclcpp/logging.hpp"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/scope_exit.hpp"
#include "rcl/error_handling.h"
#include "rcl/rcl.h"
#include "rmw/error_handling.h"
#include "rmw/rmw.h"
#include "rcutils/logging_macros.h"
// Determine if sigaction is available
#if __APPLE__ || _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE
#define HAS_SIGACTION
#endif
/// Mutex to protect installation and uninstallation of signal handlers.
static std::mutex g_signal_handlers_mutex;
/// Atomic bool to control setup of signal handlers.
static std::atomic<bool> g_signal_handlers_installed(false);
/// Mutex to protect g_sigint_guard_cond_handles
static std::mutex g_sigint_guard_cond_handles_mutex;
/// Guard conditions for interrupting the rmw implementation when the global interrupt signal fired.
static std::map<rcl_wait_set_t *, rcl_guard_condition_t> g_sigint_guard_cond_handles;
/// Condition variable for timed sleep (see sleep_for).
static std::condition_variable g_interrupt_condition_variable;
/// Mutex for protecting the global condition variable.
static std::mutex g_interrupt_mutex;
#ifdef HAS_SIGACTION
static struct sigaction old_action;
#else
typedef void (* signal_handler_t)(int);
static signal_handler_t old_signal_handler = 0;
#endif
#ifdef HAS_SIGACTION
struct sigaction
set_sigaction(int signal_value, const struct sigaction & action)
#else
signal_handler_t
set_signal_handler(int signal_value, signal_handler_t signal_handler)
#endif
{
#ifdef HAS_SIGACTION
struct sigaction old_action;
ssize_t ret = sigaction(signal_value, &action, &old_action);
if (ret == -1)
#else
signal_handler_t old_signal_handler = std::signal(signal_value, signal_handler);
// NOLINTNEXTLINE(readability/braces)
if (old_signal_handler == SIG_ERR)
#endif
{
const size_t error_length = 1024;
// NOLINTNEXTLINE(runtime/arrays)
char error_string[error_length];
#ifndef _WIN32
#if (defined(_GNU_SOURCE) && !defined(ANDROID))
char * msg = strerror_r(errno, error_string, error_length);
if (msg != error_string) {
strncpy(error_string, msg, error_length);
msg[error_length - 1] = '\0';
}
#else
int error_status = strerror_r(errno, error_string, error_length);
if (error_status != 0) {
throw std::runtime_error("Failed to get error string for errno: " + std::to_string(errno));
}
#endif
#else
strerror_s(error_string, error_length, errno);
#endif
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
throw std::runtime_error(
std::string("Failed to set SIGINT signal handler: (" + std::to_string(errno) + ")") +
error_string);
// *INDENT-ON*
}
#ifdef HAS_SIGACTION
return old_action;
#else
return old_signal_handler;
#endif
}
void
#ifdef HAS_SIGACTION
signal_handler(int signal_value, siginfo_t * siginfo, void * context)
#else
signal_handler(int signal_value)
#endif
{
RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "signal_handler(signal_value=%d)", signal_value);
#ifdef HAS_SIGACTION
if (old_action.sa_flags & SA_SIGINFO) {
if (old_action.sa_sigaction != NULL) {
old_action.sa_sigaction(signal_value, siginfo, context);
}
} else {
if (
old_action.sa_handler != NULL && // Is set
old_action.sa_handler != SIG_DFL && // Is not default
old_action.sa_handler != SIG_IGN) // Is not ignored
{
old_action.sa_handler(signal_value);
}
}
#else
if (old_signal_handler) {
old_signal_handler(signal_value);
}
#endif
for (auto context_ptr : rclcpp::get_contexts()) {
if (context_ptr->get_init_options().shutdown_on_sigint) {
// do not notify all, instead do that once after all are shutdown
context_ptr->shutdown("signal handler", false /* notify_all */);
}
}
rclcpp::notify_all();
}
void
rclcpp::init(int argc, char const * const argv[], const rclcpp::InitOptions & init_options)
{
@@ -165,46 +36,19 @@ rclcpp::init(int argc, char const * const argv[], const rclcpp::InitOptions & in
bool
rclcpp::install_signal_handlers()
{
std::lock_guard<std::mutex> lock(g_signal_handlers_mutex);
bool already_installed = g_signal_handlers_installed.exchange(true);
if (already_installed) {
return false;
}
#ifdef HAS_SIGACTION
struct sigaction action;
memset(&action, 0, sizeof(action));
sigemptyset(&action.sa_mask);
action.sa_sigaction = ::signal_handler;
action.sa_flags = SA_SIGINFO;
::old_action = set_sigaction(SIGINT, action);
#else
::old_signal_handler = set_signal_handler(SIGINT, ::signal_handler);
#endif
RCLCPP_DEBUG(rclcpp::get_logger("rclcpp"), "signal handler installed");
return true;
return rclcpp::SignalHandler::get_global_signal_handler().install();
}
bool
rclcpp::signal_handlers_installed()
{
return g_signal_handlers_installed.load();
return rclcpp::SignalHandler::get_global_signal_handler().is_installed();
}
bool
rclcpp::uninstall_signal_handlers()
{
std::lock_guard<std::mutex> lock(g_signal_handlers_mutex);
bool installed = g_signal_handlers_installed.exchange(false);
if (!installed) {
return false;
}
#ifdef HAS_SIGACTION
set_sigaction(SIGINT, ::old_action);
#else
set_signal_handler(SIGINT, ::old_signal_handler);
#endif
RCLCPP_DEBUG(rclcpp::get_logger("rclcpp"), "signal handler uninstalled");
return true;
return rclcpp::SignalHandler::get_global_signal_handler().uninstall();
}
std::vector<std::string>
@@ -300,14 +144,10 @@ rclcpp::shutdown(rclcpp::Context::SharedPtr context, const std::string & reason)
if (nullptr == context) {
context = default_context;
}
bool ret = context->shutdown(reason);
// Uninstall the signal handlers if this is the default context's shutdown.
if (context == default_context) {
uninstall_signal_handlers();
rclcpp::uninstall_signal_handlers();
}
return ret;
}
@@ -321,84 +161,14 @@ rclcpp::on_shutdown(std::function<void()> callback, rclcpp::Context::SharedPtr c
context->on_shutdown(callback);
}
rcl_guard_condition_t *
rclcpp::get_sigint_guard_condition(rcl_wait_set_t * wait_set, rclcpp::Context::SharedPtr context)
{
std::lock_guard<std::mutex> lock(g_sigint_guard_cond_handles_mutex);
auto kv = g_sigint_guard_cond_handles.find(wait_set);
if (kv != g_sigint_guard_cond_handles.end()) {
return &kv->second;
} else {
using rclcpp::contexts::default_context::get_global_default_context;
if (nullptr == context) {
context = get_global_default_context();
}
rcl_guard_condition_t handle = rcl_get_zero_initialized_guard_condition();
rcl_guard_condition_options_t options = rcl_guard_condition_get_default_options();
auto ret = rcl_guard_condition_init(&handle, context->get_rcl_context().get(), options);
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "Couldn't initialize guard condition: ");
}
g_sigint_guard_cond_handles[wait_set] = handle;
return &g_sigint_guard_cond_handles[wait_set];
}
}
void
rclcpp::release_sigint_guard_condition(rcl_wait_set_t * wait_set)
{
std::lock_guard<std::mutex> lock(g_sigint_guard_cond_handles_mutex);
auto kv = g_sigint_guard_cond_handles.find(wait_set);
if (kv != g_sigint_guard_cond_handles.end()) {
if (rcl_guard_condition_fini(&kv->second) != RCL_RET_OK) {
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
throw std::runtime_error(std::string(
"Failed to destroy sigint guard condition: ") +
rcl_get_error_string().str);
// *INDENT-ON*
}
g_sigint_guard_cond_handles.erase(kv);
} else {
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
throw std::runtime_error(std::string(
"Tried to release sigint guard condition for nonexistent wait set"));
// *INDENT-ON*
}
}
bool
rclcpp::sleep_for(const std::chrono::nanoseconds & nanoseconds, rclcpp::Context::SharedPtr context)
{
std::chrono::nanoseconds time_left = nanoseconds;
{
std::unique_lock<std::mutex> lock(::g_interrupt_mutex);
auto start = std::chrono::steady_clock::now();
// this will release the lock while waiting
::g_interrupt_condition_variable.wait_for(lock, nanoseconds);
time_left -= std::chrono::steady_clock::now() - start;
using rclcpp::contexts::default_context::get_global_default_context;
if (nullptr == context) {
context = get_global_default_context();
}
if (time_left > std::chrono::nanoseconds::zero() && ok(context)) {
return sleep_for(time_left);
}
// Return true if the timeout elapsed successfully, otherwise false.
return ok(context);
}
void
rclcpp::notify_all()
{
{
std::lock_guard<std::mutex> lock(g_sigint_guard_cond_handles_mutex);
for (auto & kv : g_sigint_guard_cond_handles) {
rcl_ret_t status = rcl_trigger_guard_condition(&(kv.second));
if (status != RCL_RET_OK) {
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"failed to trigger guard condition: %s", rcl_get_error_string().str);
}
}
}
g_interrupt_condition_variable.notify_all();
return context->sleep_for(nanoseconds);
}
const char *

View File

@@ -0,0 +1,71 @@
// Copyright 2018 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 <cstdio>
#include <map>
#include <string>
#include "gtest/gtest.h"
#include "rclcpp/rclcpp.hpp"
TEST(test_local_parameters, set_parameter_if_not_set) {
auto node = rclcpp::Node::make_shared("test_local_parameters_set_parameter_if_not_set");
{
// try to set a map of parameters
std::map<std::string, double> bar_map{
{"x", 0.5},
{"y", 1.0},
};
node->set_parameters_if_not_set("bar", bar_map);
double bar_x_value;
ASSERT_TRUE(node->get_parameter("bar.x", bar_x_value));
EXPECT_EQ(bar_x_value, 0.5);
double bar_y_value;
ASSERT_TRUE(node->get_parameter("bar.y", bar_y_value));
EXPECT_EQ(bar_y_value, 1.0);
std::map<std::string, double> new_map;
ASSERT_TRUE(node->get_parameters("bar", new_map));
ASSERT_EQ(new_map.size(), 2U);
EXPECT_EQ(new_map["x"], 0.5);
EXPECT_EQ(new_map["y"], 1.0);
}
{
// try to get a map of parameters that doesn't exist
std::map<std::string, double> no_exist_map;
ASSERT_FALSE(node->get_parameters("no_exist", no_exist_map));
}
{
// set parameters for a map with different types, then try to get them back as a map
node->set_parameter_if_not_set("baz.x", 1.0);
node->set_parameter_if_not_set("baz.y", "hello");
std::map<std::string, double> baz_map;
EXPECT_THROW(node->get_parameters("baz", baz_map), rclcpp::ParameterTypeException);
}
}
int main(int argc, char ** argv)
{
::setvbuf(stdout, NULL, _IONBF, BUFSIZ);
// NOTE: use custom main to ensure that rclcpp::init is called only once
rclcpp::init(argc, argv);
::testing::InitGoogleTest(&argc, argv);
int ret = RUN_ALL_TESTS();
rclcpp::shutdown();
return ret;
}

View File

@@ -112,7 +112,7 @@ TEST_F(TestTimeSource, reattach) {
ASSERT_NO_THROW(ts.attachNode(node));
}
TEST_F(TestTimeSource, ROS_time_valid) {
TEST_F(TestTimeSource, ROS_time_valid_attach_detach) {
rclcpp::TimeSource ts;
auto ros_clock = std::make_shared<rclcpp::Clock>(RCL_ROS_TIME);
@@ -131,6 +131,37 @@ TEST_F(TestTimeSource, ROS_time_valid) {
EXPECT_FALSE(ros_clock->ros_time_is_active());
}
TEST_F(TestTimeSource, ROS_time_valid_wall_time) {
rclcpp::TimeSource ts;
auto ros_clock = std::make_shared<rclcpp::Clock>(RCL_ROS_TIME);
auto ros_clock2 = std::make_shared<rclcpp::Clock>(RCL_ROS_TIME);
ts.attachClock(ros_clock);
EXPECT_FALSE(ros_clock->ros_time_is_active());
ts.attachNode(node);
EXPECT_FALSE(ros_clock->ros_time_is_active());
ts.attachClock(ros_clock2);
EXPECT_FALSE(ros_clock2->ros_time_is_active());
}
TEST_F(TestTimeSource, ROS_time_valid_sim_time) {
rclcpp::TimeSource ts;
auto ros_clock = std::make_shared<rclcpp::Clock>(RCL_ROS_TIME);
auto ros_clock2 = std::make_shared<rclcpp::Clock>(RCL_ROS_TIME);
ts.attachClock(ros_clock);
EXPECT_FALSE(ros_clock->ros_time_is_active());
node->set_parameter_if_not_set("use_sim_time", true);
ts.attachNode(node);
EXPECT_TRUE(ros_clock->ros_time_is_active());
ts.attachClock(ros_clock2);
EXPECT_TRUE(ros_clock2->ros_time_is_active());
}
TEST_F(TestTimeSource, clock) {
rclcpp::TimeSource ts(node);
auto ros_clock = std::make_shared<rclcpp::Clock>(RCL_ROS_TIME);

View File

@@ -2,6 +2,22 @@
Changelog for package rclcpp_action
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
0.6.5 (2019-12-05)
------------------
* Do not throw exception in action client if take fails (`#888 <https://github.com/ros2/rclcpp/issues/888>`_) (`#905 <https://github.com/ros2/rclcpp/issues/905>`_)
* Contributors: Jacob Perron
0.6.4 (2019-04-05)
------------------
* Wait for action server before sending goal (`#686 <https://github.com/ros2/rclcpp/issues/686>`_)
* Contributors: Jacob Perron, Shane Loretz
0.6.3 (2019-02-08)
------------------
0.6.2 (2018-12-13)
------------------
0.6.1 (2018-12-07)
------------------
* Added wait_for_action_server() for action clients (`#598 <https://github.com/ros2/rclcpp/issues/598>`_)

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>0.6.1</version>
<version>0.6.5</version>
<description>Adds action APIs for C++.</description>
<maintainer email="dthomas@osrfoundation.org">Dirk Thomas</maintainer>
<license>Apache License 2.0</license>

View File

@@ -388,20 +388,20 @@ ClientBase::execute()
rcl_ret_t ret = rcl_action_take_feedback(
pimpl_->client_handle.get(), feedback_message.get());
pimpl_->is_feedback_ready = false;
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "error taking feedback");
} else {
if (RCL_RET_OK == ret) {
this->handle_feedback_message(feedback_message);
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "error taking feedback");
}
} else if (pimpl_->is_status_ready) {
std::shared_ptr<void> status_message = this->create_status_message();
rcl_ret_t ret = rcl_action_take_status(
pimpl_->client_handle.get(), status_message.get());
pimpl_->is_status_ready = false;
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "error taking status");
} else {
if (RCL_RET_OK == ret) {
this->handle_status_message(status_message);
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "error taking status");
}
} else if (pimpl_->is_goal_response_ready) {
rmw_request_id_t response_header;
@@ -409,10 +409,10 @@ ClientBase::execute()
rcl_ret_t ret = rcl_action_take_goal_response(
pimpl_->client_handle.get(), &response_header, goal_response.get());
pimpl_->is_goal_response_ready = false;
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "error taking goal response");
} else {
if (RCL_RET_OK == ret) {
this->handle_goal_response(response_header, goal_response);
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "error taking goal response");
}
} else if (pimpl_->is_result_response_ready) {
rmw_request_id_t response_header;
@@ -420,10 +420,10 @@ ClientBase::execute()
rcl_ret_t ret = rcl_action_take_result_response(
pimpl_->client_handle.get(), &response_header, result_response.get());
pimpl_->is_result_response_ready = false;
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "error taking result response");
} else {
if (RCL_RET_OK == ret) {
this->handle_result_response(response_header, result_response);
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "error taking result response");
}
} else if (pimpl_->is_cancel_response_ready) {
rmw_request_id_t response_header;
@@ -431,10 +431,10 @@ ClientBase::execute()
rcl_ret_t ret = rcl_action_take_cancel_response(
pimpl_->client_handle.get(), &response_header, cancel_response.get());
pimpl_->is_cancel_response_ready = false;
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "error taking cancel response");
} else {
if (RCL_RET_OK == ret) {
this->handle_cancel_response(response_header, cancel_response);
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "error taking cancel response");
}
} else {
throw std::runtime_error("Executing action client but nothing is ready");

View File

@@ -47,6 +47,8 @@
using namespace std::chrono_literals;
const auto WAIT_FOR_SERVER_TIMEOUT = 10s;
class TestClient : public ::testing::Test
{
protected:
@@ -269,6 +271,7 @@ TEST_F(TestClient, construction_and_destruction)
TEST_F(TestClient, async_send_goal_but_ignore_feedback_and_result)
{
auto action_client = rclcpp_action::create_client<ActionType>(client_node, action_name);
ASSERT_TRUE(action_client->wait_for_action_server(WAIT_FOR_SERVER_TIMEOUT));
ActionGoal bad_goal;
bad_goal.order = -5;
@@ -290,6 +293,7 @@ TEST_F(TestClient, async_send_goal_but_ignore_feedback_and_result)
TEST_F(TestClient, async_send_goal_and_ignore_feedback_but_wait_for_result)
{
auto action_client = rclcpp_action::create_client<ActionType>(client_node, action_name);
ASSERT_TRUE(action_client->wait_for_action_server(WAIT_FOR_SERVER_TIMEOUT));
ActionGoal goal;
goal.order = 5;
@@ -311,6 +315,7 @@ TEST_F(TestClient, async_send_goal_and_ignore_feedback_but_wait_for_result)
TEST_F(TestClient, async_send_goal_with_feedback_and_result)
{
auto action_client = rclcpp_action::create_client<ActionType>(client_node, action_name);
ASSERT_TRUE(action_client->wait_for_action_server(WAIT_FOR_SERVER_TIMEOUT));
ActionGoal goal;
goal.order = 4;
@@ -342,6 +347,7 @@ TEST_F(TestClient, async_send_goal_with_feedback_and_result)
TEST_F(TestClient, async_cancel_one_goal)
{
auto action_client = rclcpp_action::create_client<ActionType>(client_node, action_name);
ASSERT_TRUE(action_client->wait_for_action_server(WAIT_FOR_SERVER_TIMEOUT));
ActionGoal goal;
goal.order = 5;
@@ -359,6 +365,7 @@ TEST_F(TestClient, async_cancel_one_goal)
TEST_F(TestClient, async_cancel_all_goals)
{
auto action_client = rclcpp_action::create_client<ActionType>(client_node, action_name);
ASSERT_TRUE(action_client->wait_for_action_server(WAIT_FOR_SERVER_TIMEOUT));
ActionGoal goal;
goal.order = 6;
@@ -393,6 +400,7 @@ TEST_F(TestClient, async_cancel_all_goals)
TEST_F(TestClient, async_cancel_some_goals)
{
auto action_client = rclcpp_action::create_client<ActionType>(client_node, action_name);
ASSERT_TRUE(action_client->wait_for_action_server(WAIT_FOR_SERVER_TIMEOUT));
ActionGoal goal;
goal.order = 6;

View File

@@ -2,6 +2,18 @@
Changelog for package rclcpp_lifecycle
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
0.6.5 (2019-12-05)
------------------
0.6.4 (2019-04-06)
------------------
0.6.3 (2019-02-08)
------------------
0.6.2 (2018-12-13)
------------------
0.6.1 (2018-12-07)
------------------
* Added node path and time stamp to parameter event message (`#584 <https://github.com/ros2/rclcpp/issues/584>`_)

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>0.6.1</version>
<version>0.6.5</version>
<description>Package containing a prototype for lifecycle implementation</description>
<maintainer email="karsten@osrfoundation.org">Karsten Knese</maintainer>
<license>Apache License 2.0</license>