Skip to content

Commit

Permalink
Hook up the incompatible type event inside of rclcpp (ros2#2069)
Browse files Browse the repository at this point in the history
* Rename QOSEventHandler* to EventHandler.

We are going to be using it for more than just QOS events, so
rename it to just EventHandler and EventHandlerBase for now.
Leave the old names in place but deprecated.

* Rename qos_event.hpp -> event_handler.hpp
* Revamp incompatible_qos callback setting.
* Add in incompatible type implementation.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
  • Loading branch information
clalancette authored and Barry-Xu-2018 committed Jan 12, 2024
1 parent 30af7d1 commit 4e80747
Show file tree
Hide file tree
Showing 13 changed files with 437 additions and 327 deletions.
2 changes: 1 addition & 1 deletion rclcpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ set(${PROJECT_NAME}_SRCS
src/rclcpp/parameter_value.cpp
src/rclcpp/publisher_base.cpp
src/rclcpp/qos.cpp
src/rclcpp/qos_event.cpp
src/rclcpp/event_handler.cpp
src/rclcpp/qos_overriding_options.cpp
src/rclcpp/serialization.cpp
src/rclcpp/serialized_message.cpp
Expand Down
306 changes: 306 additions & 0 deletions rclcpp/include/rclcpp/event_handler.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,306 @@
// Copyright 2019 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__EVENT_HANDLER_HPP_
#define RCLCPP__EVENT_HANDLER_HPP_

#include <functional>
#include <memory>
#include <mutex>
#include <stdexcept>
#include <string>

#include "rcl/error_handling.h"
#include "rcl/event_callback.h"
#include "rmw/impl/cpp/demangle.hpp"
#include "rmw/incompatible_qos_events_statuses.h"
#include "rmw/events_statuses/incompatible_type.h"

#include "rcutils/logging_macros.h"

#include "rclcpp/detail/cpp_callback_trampoline.hpp"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/function_traits.hpp"
#include "rclcpp/logging.hpp"
#include "rclcpp/waitable.hpp"

namespace rclcpp
{

using QOSDeadlineRequestedInfo = rmw_requested_deadline_missed_status_t;
using QOSDeadlineOfferedInfo = rmw_offered_deadline_missed_status_t;
using QOSLivelinessChangedInfo = rmw_liveliness_changed_status_t;
using QOSLivelinessLostInfo = rmw_liveliness_lost_status_t;
using QOSMessageLostInfo = rmw_message_lost_status_t;
using QOSOfferedIncompatibleQoSInfo = rmw_offered_qos_incompatible_event_status_t;
using QOSRequestedIncompatibleQoSInfo = rmw_requested_qos_incompatible_event_status_t;

using IncompatibleTypeInfo = rmw_incompatible_type_status_t;

using QOSDeadlineRequestedCallbackType = std::function<void (QOSDeadlineRequestedInfo &)>;
using QOSDeadlineOfferedCallbackType = std::function<void (QOSDeadlineOfferedInfo &)>;
using QOSLivelinessChangedCallbackType = std::function<void (QOSLivelinessChangedInfo &)>;
using QOSLivelinessLostCallbackType = std::function<void (QOSLivelinessLostInfo &)>;
using QOSMessageLostCallbackType = std::function<void (QOSMessageLostInfo &)>;
using QOSOfferedIncompatibleQoSCallbackType = std::function<void (QOSOfferedIncompatibleQoSInfo &)>;
using QOSRequestedIncompatibleQoSCallbackType =
std::function<void (QOSRequestedIncompatibleQoSInfo &)>;

using IncompatibleTypeCallbackType = std::function<void (IncompatibleTypeInfo &)>;

/// Contains callbacks for various types of events a Publisher can receive from the middleware.
struct PublisherEventCallbacks
{
QOSDeadlineOfferedCallbackType deadline_callback;
QOSLivelinessLostCallbackType liveliness_callback;
QOSOfferedIncompatibleQoSCallbackType incompatible_qos_callback;
IncompatibleTypeCallbackType incompatible_type_callback;
};

/// Contains callbacks for non-message events that a Subscription can receive from the middleware.
struct SubscriptionEventCallbacks
{
QOSDeadlineRequestedCallbackType deadline_callback;
QOSLivelinessChangedCallbackType liveliness_callback;
QOSRequestedIncompatibleQoSCallbackType incompatible_qos_callback;
QOSMessageLostCallbackType message_lost_callback;
IncompatibleTypeCallbackType incompatible_type_callback;
};

class UnsupportedEventTypeException : public exceptions::RCLErrorBase, public std::runtime_error
{
public:
RCLCPP_PUBLIC
UnsupportedEventTypeException(
rcl_ret_t ret,
const rcl_error_state_t * error_state,
const std::string & prefix);

RCLCPP_PUBLIC
UnsupportedEventTypeException(
const exceptions::RCLErrorBase & base_exc,
const std::string & prefix);
};

class EventHandlerBase : public Waitable
{
public:
enum class EntityType : std::size_t
{
Event,
};

RCLCPP_PUBLIC
virtual ~EventHandlerBase();

/// Get the number of ready events
RCLCPP_PUBLIC
size_t
get_number_of_ready_events() override;

/// Add the Waitable to a wait set.
RCLCPP_PUBLIC
void
add_to_wait_set(rcl_wait_set_t * wait_set) override;

/// Check if the Waitable is ready.
RCLCPP_PUBLIC
bool
is_ready(rcl_wait_set_t * wait_set) override;

/// Set a callback to be called when each new event instance occurs.
/**
* The callback receives a size_t which is the number of events that occurred
* since the last time this callback was called.
* Normally this is 1, but can be > 1 if events occurred before any
* callback was set.
*
* The callback also receives an int identifier argument.
* This is needed because a Waitable may be composed of several distinct entities,
* such as subscriptions, services, etc.
* The application should provide a generic callback function that will be then
* forwarded by the waitable to all of its entities.
* Before forwarding, a different value for the identifier argument will be
* bond to the function.
* This implies that the provided callback can use the identifier to behave
* differently depending on which entity triggered the waitable to become ready.
*
* Since this callback is called from the middleware, you should aim to make
* it fast and not blocking.
* If you need to do a lot of work or wait for some other event, you should
* spin it off to another thread, otherwise you risk blocking the middleware.
*
* Calling it again will clear any previously set callback.
*
* An exception will be thrown if the callback is not callable.
*
* This function is thread-safe.
*
* If you want more information available in the callback, like the qos event
* or other information, you may use a lambda with captures or std::bind.
*
* \sa rmw_event_set_callback
* \sa rcl_event_set_callback
*
* \param[in] callback functor to be called when a new event occurs
*/
void
set_on_ready_callback(std::function<void(size_t, int)> callback) override
{
if (!callback) {
throw std::invalid_argument(
"The callback passed to set_on_ready_callback "
"is not callable.");
}

// Note: we bind the int identifier argument to this waitable's entity types
auto new_callback =
[callback, this](size_t number_of_events) {
try {
callback(number_of_events, static_cast<int>(EntityType::Event));
} catch (const std::exception & exception) {
RCLCPP_ERROR_STREAM(
// TODO(wjwwood): get this class access to the node logger it is associated with
rclcpp::get_logger("rclcpp"),
"rclcpp::EventHandlerBase@" << this <<
" caught " << rmw::impl::cpp::demangle(exception) <<
" exception in user-provided callback for the 'on ready' callback: " <<
exception.what());
} catch (...) {
RCLCPP_ERROR_STREAM(
rclcpp::get_logger("rclcpp"),
"rclcpp::EventHandlerBase@" << this <<
" caught unhandled exception in user-provided callback " <<
"for the 'on ready' callback");
}
};

std::lock_guard<std::recursive_mutex> lock(callback_mutex_);

// Set it temporarily to the new callback, while we replace the old one.
// This two-step setting, prevents a gap where the old std::function has
// been replaced but the middleware hasn't been told about the new one yet.
set_on_new_event_callback(
rclcpp::detail::cpp_callback_trampoline<decltype(new_callback), const void *, size_t>,
static_cast<const void *>(&new_callback));

// Store the std::function to keep it in scope, also overwrites the existing one.
on_new_event_callback_ = new_callback;

// Set it again, now using the permanent storage.
set_on_new_event_callback(
rclcpp::detail::cpp_callback_trampoline<
decltype(on_new_event_callback_), const void *, size_t>,
static_cast<const void *>(&on_new_event_callback_));
}

/// Unset the callback registered for new events, if any.
void
clear_on_ready_callback() override
{
std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
if (on_new_event_callback_) {
set_on_new_event_callback(nullptr, nullptr);
on_new_event_callback_ = nullptr;
}
}

protected:
RCLCPP_PUBLIC
void
set_on_new_event_callback(rcl_event_callback_t callback, const void * user_data);

std::recursive_mutex callback_mutex_;
std::function<void(size_t)> on_new_event_callback_{nullptr};

rcl_event_t event_handle_;
size_t wait_set_event_index_;
};

using QOSEventHandlerBase [[deprecated("Use rclcpp::EventHandlerBase")]] = EventHandlerBase;

template<typename EventCallbackT, typename ParentHandleT>
class EventHandler : public EventHandlerBase
{
public:
template<typename InitFuncT, typename EventTypeEnum>
EventHandler(
const EventCallbackT & callback,
InitFuncT init_func,
ParentHandleT parent_handle,
EventTypeEnum event_type)
: parent_handle_(parent_handle), event_callback_(callback)
{
event_handle_ = rcl_get_zero_initialized_event();
rcl_ret_t ret = init_func(&event_handle_, parent_handle.get(), event_type);
if (ret != RCL_RET_OK) {
if (ret == RCL_RET_UNSUPPORTED) {
UnsupportedEventTypeException exc(ret, rcl_get_error_state(), "Failed to initialize event");
rcl_reset_error();
throw exc;
} else {
rclcpp::exceptions::throw_from_rcl_error(ret, "Failed to initialize event");
}
}
}

/// Take data so that the callback cannot be scheduled again
std::shared_ptr<void>
take_data() override
{
EventCallbackInfoT callback_info;
rcl_ret_t ret = rcl_take_event(&event_handle_, &callback_info);
if (ret != RCL_RET_OK) {
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"Couldn't take event info: %s", rcl_get_error_string().str);
return nullptr;
}
return std::static_pointer_cast<void>(std::make_shared<EventCallbackInfoT>(callback_info));
}

std::shared_ptr<void>
take_data_by_entity_id(size_t id) override
{
(void)id;
return take_data();
}

/// Execute any entities of the Waitable that are ready.
void
execute(std::shared_ptr<void> & data) override
{
if (!data) {
throw std::runtime_error("'data' is empty");
}
auto callback_ptr = std::static_pointer_cast<EventCallbackInfoT>(data);
event_callback_(*callback_ptr);
callback_ptr.reset();
}

private:
using EventCallbackInfoT = typename std::remove_reference<typename
rclcpp::function_traits::function_traits<EventCallbackT>::template argument_type<0>>::type;

ParentHandleT parent_handle_;
EventCallbackT event_callback_;
};

template<typename EventCallbackT, typename ParentHandleT>
using QOSEventHandler [[deprecated("Use rclcpp::EventHandler")]] = EventHandler<EventCallbackT,
ParentHandleT>;

} // namespace rclcpp

#endif // RCLCPP__EVENT_HANDLER_HPP_
13 changes: 8 additions & 5 deletions rclcpp/include/rclcpp/publisher_base.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
#include "rclcpp/macros.hpp"
#include "rclcpp/network_flow_endpoint.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/qos_event.hpp"
#include "rclcpp/event_handler.hpp"
#include "rclcpp/type_support_decl.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rcpputils/time.hpp"
Expand Down Expand Up @@ -124,7 +124,7 @@ class PublisherBase : public std::enable_shared_from_this<PublisherBase>
/** \return The map of QoS event handlers. */
RCLCPP_PUBLIC
const
std::unordered_map<rcl_publisher_event_type_t, std::shared_ptr<rclcpp::QOSEventHandlerBase>> &
std::unordered_map<rcl_publisher_event_type_t, std::shared_ptr<rclcpp::EventHandlerBase>> &
get_event_handlers() const;

/// Get subscription count
Expand Down Expand Up @@ -276,7 +276,7 @@ class PublisherBase : public std::enable_shared_from_this<PublisherBase>
* If you want more information available in the callback, like the qos event
* or other information, you may use a lambda with captures or std::bind.
*
* \sa rclcpp::QOSEventHandlerBase::set_on_ready_callback
* \sa rclcpp::EventHandlerBase::set_on_ready_callback
*
* \param[in] callback functor to be called when a new event occurs
* \param[in] event_type identifier for the qos event we want to attach the callback to
Expand Down Expand Up @@ -327,7 +327,7 @@ class PublisherBase : public std::enable_shared_from_this<PublisherBase>
const EventCallbackT & callback,
const rcl_publisher_event_type_t event_type)
{
auto handler = std::make_shared<QOSEventHandler<EventCallbackT,
auto handler = std::make_shared<EventHandler<EventCallbackT,
std::shared_ptr<rcl_publisher_t>>>(
callback,
rcl_publisher_event_init,
Expand All @@ -339,12 +339,15 @@ class PublisherBase : public std::enable_shared_from_this<PublisherBase>
RCLCPP_PUBLIC
void default_incompatible_qos_callback(QOSOfferedIncompatibleQoSInfo & info) const;

RCLCPP_PUBLIC
void default_incompatible_type_callback(IncompatibleTypeInfo & info) const;

std::shared_ptr<rcl_node_t> rcl_node_handle_;

std::shared_ptr<rcl_publisher_t> publisher_handle_;

std::unordered_map<rcl_publisher_event_type_t,
std::shared_ptr<rclcpp::QOSEventHandlerBase>> event_handlers_;
std::shared_ptr<rclcpp::EventHandlerBase>> event_handlers_;

using IntraProcessManagerWeakPtr =
std::weak_ptr<rclcpp::experimental::IntraProcessManager>;
Expand Down
2 changes: 1 addition & 1 deletion rclcpp/include/rclcpp/publisher_options.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
#include "rclcpp/detail/rmw_implementation_specific_publisher_payload.hpp"
#include "rclcpp/intra_process_setting.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/qos_event.hpp"
#include "rclcpp/event_handler.hpp"
#include "rclcpp/qos_overriding_options.hpp"

namespace rclcpp
Expand Down
Loading

0 comments on commit 4e80747

Please sign in to comment.