15 #ifndef RCLCPP__EXPERIMENTAL__INTRA_PROCESS_MANAGER_HPP_
16 #define RCLCPP__EXPERIMENTAL__INTRA_PROCESS_MANAGER_HPP_
18 #include <rmw/types.h>
20 #include <shared_mutex>
26 #include <unordered_map>
31 #include "rclcpp/allocator/allocator_deleter.hpp"
32 #include "rclcpp/experimental/buffers/intra_process_buffer.hpp"
33 #include "rclcpp/experimental/ros_message_intra_process_buffer.hpp"
34 #include "rclcpp/experimental/subscription_intra_process.hpp"
35 #include "rclcpp/experimental/subscription_intra_process_base.hpp"
36 #include "rclcpp/experimental/subscription_intra_process_buffer.hpp"
37 #include "rclcpp/logger.hpp"
38 #include "rclcpp/logging.hpp"
39 #include "rclcpp/macros.hpp"
40 #include "rclcpp/publisher_base.hpp"
41 #include "rclcpp/type_adapter.hpp"
42 #include "rclcpp/visibility_control.hpp"
47 namespace experimental
118 typename ROSMessageType,
119 typename Alloc = std::allocator<ROSMessageType>
123 const rclcpp::experimental::SubscriptionIntraProcessBase::SharedPtr & subscription)
125 std::unique_lock<std::shared_timed_mutex> lock(mutex_);
127 uint64_t sub_id = IntraProcessManager::get_next_unique_id();
129 subscriptions_[sub_id] = subscription;
132 for (
auto & pair : publishers_) {
133 auto publisher = pair.second.lock();
137 if (can_communicate(publisher, subscription)) {
138 uint64_t pub_id = pair.first;
139 insert_sub_id_for_pub(sub_id, pub_id, subscription->use_take_shared_method());
140 if (publisher->is_durability_transient_local() &&
141 subscription->is_durability_transient_local())
143 do_transient_local_publish<ROSMessageType, Alloc>(
145 subscription->use_take_shared_method());
180 const rclcpp::PublisherBase::SharedPtr & publisher,
181 const rclcpp::experimental::buffers::IntraProcessBufferBase::SharedPtr & buffer =
182 rclcpp::experimental::buffers::IntraProcessBufferBase::SharedPtr());
220 typename ROSMessageType,
222 typename Deleter = std::default_delete<MessageT>
226 uint64_t intra_process_publisher_id,
227 std::unique_ptr<MessageT, Deleter> message,
228 typename allocator::AllocRebind<MessageT, Alloc>::allocator_type & allocator)
230 using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
231 using MessageAllocatorT =
typename MessageAllocTraits::allocator_type;
233 std::shared_lock<std::shared_timed_mutex> lock(mutex_);
235 auto publisher_it = pub_to_subs_.find(intra_process_publisher_id);
236 if (publisher_it == pub_to_subs_.end()) {
240 "Calling do_intra_process_publish for invalid or no longer existing publisher id");
243 const auto & sub_ids = publisher_it->second;
245 if (sub_ids.take_ownership_subscriptions.empty()) {
247 std::shared_ptr<MessageT> msg = std::move(message);
249 this->
template add_shared_msg_to_buffers<MessageT, Alloc, Deleter, ROSMessageType>(
250 msg, sub_ids.take_shared_subscriptions);
251 }
else if (!sub_ids.take_ownership_subscriptions.empty() &&
252 sub_ids.take_shared_subscriptions.size() <= 1)
258 std::vector<uint64_t> concatenated_vector(
259 sub_ids.take_shared_subscriptions.begin(), sub_ids.take_shared_subscriptions.end());
260 concatenated_vector.insert(
261 concatenated_vector.end(),
262 sub_ids.take_ownership_subscriptions.begin(),
263 sub_ids.take_ownership_subscriptions.end());
264 this->
template add_owned_msg_to_buffers<MessageT, Alloc, Deleter, ROSMessageType>(
268 }
else if (!sub_ids.take_ownership_subscriptions.empty() &&
269 sub_ids.take_shared_subscriptions.size() > 1)
273 auto shared_msg = std::allocate_shared<MessageT, MessageAllocatorT>(allocator, *message);
275 this->
template add_shared_msg_to_buffers<MessageT, Alloc, Deleter, ROSMessageType>(
276 shared_msg, sub_ids.take_shared_subscriptions);
277 this->
template add_owned_msg_to_buffers<MessageT, Alloc, Deleter, ROSMessageType>(
278 std::move(message), sub_ids.take_ownership_subscriptions, allocator);
284 typename ROSMessageType,
286 typename Deleter = std::default_delete<MessageT>
288 std::shared_ptr<const MessageT>
289 do_intra_process_publish_and_return_shared(
290 uint64_t intra_process_publisher_id,
291 std::unique_ptr<MessageT, Deleter> message,
292 typename allocator::AllocRebind<MessageT, Alloc>::allocator_type & allocator)
294 using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
295 using MessageAllocatorT =
typename MessageAllocTraits::allocator_type;
297 std::shared_lock<std::shared_timed_mutex> lock(mutex_);
299 auto publisher_it = pub_to_subs_.find(intra_process_publisher_id);
300 if (publisher_it == pub_to_subs_.end()) {
304 "Calling do_intra_process_publish for invalid or no longer existing publisher id");
307 const auto & sub_ids = publisher_it->second;
309 if (sub_ids.take_ownership_subscriptions.empty()) {
311 std::shared_ptr<MessageT> shared_msg = std::move(message);
312 if (!sub_ids.take_shared_subscriptions.empty()) {
313 this->
template add_shared_msg_to_buffers<MessageT, Alloc, Deleter, ROSMessageType>(
314 shared_msg, sub_ids.take_shared_subscriptions);
320 auto shared_msg = std::allocate_shared<MessageT, MessageAllocatorT>(allocator, *message);
322 if (!sub_ids.take_shared_subscriptions.empty()) {
323 this->
template add_shared_msg_to_buffers<MessageT, Alloc, Deleter, ROSMessageType>(
325 sub_ids.take_shared_subscriptions);
327 if (!sub_ids.take_ownership_subscriptions.empty()) {
328 this->
template add_owned_msg_to_buffers<MessageT, Alloc, Deleter, ROSMessageType>(
330 sub_ids.take_ownership_subscriptions,
341 typename ROSMessageType>
343 add_shared_msg_to_buffer(
344 std::shared_ptr<const MessageT> message,
345 uint64_t subscription_id)
347 add_shared_msg_to_buffers<MessageT, Alloc, Deleter, ROSMessageType>(message, {subscription_id});
354 typename ROSMessageType>
356 add_owned_msg_to_buffer(
357 std::unique_ptr<MessageT, Deleter> message,
358 uint64_t subscription_id,
359 typename allocator::AllocRebind<MessageT, Alloc>::allocator_type & allocator)
361 add_owned_msg_to_buffers<MessageT, Alloc, Deleter, ROSMessageType>(
362 std::move(message), {subscription_id}, allocator);
376 rclcpp::experimental::SubscriptionIntraProcessBase::SharedPtr
377 get_subscription_intra_process(uint64_t intra_process_subscription_id);
385 struct SplittedSubscriptions
387 std::vector<uint64_t> take_shared_subscriptions;
388 std::vector<uint64_t> take_ownership_subscriptions;
394 std::size_t operator()(
const rmw_gid_t & gid)
const noexcept
397 constexpr std::size_t FNV_prime = 1099511628211u;
398 std::size_t result = 14695981039346656037u;
400 for (std::size_t i = 0; i < RMW_GID_STORAGE_SIZE; ++i) {
401 result ^= gid.data[i];
411 bool operator()(
const rmw_gid_t & lhs,
const rmw_gid_t & rhs)
const noexcept
418 std::begin(lhs.data),
420 std::begin(rhs.data));
424 using SubscriptionMap =
425 std::unordered_map<uint64_t, rclcpp::experimental::SubscriptionIntraProcessBase::WeakPtr>;
428 std::unordered_map<uint64_t, rclcpp::PublisherBase::WeakPtr>;
430 using PublisherBufferMap =
431 std::unordered_map<uint64_t, rclcpp::experimental::buffers::IntraProcessBufferBase::WeakPtr>;
433 using PublisherToSubscriptionIdsMap =
434 std::unordered_map<uint64_t, SplittedSubscriptions>;
440 rclcpp::PublisherBase::WeakPtr publisher;
443 using GidToPublisherInfoMap =
444 std::unordered_map<rmw_gid_t, PublisherInfo, rmw_gid_hash, rmw_gid_equal>;
449 get_next_unique_id();
453 insert_sub_id_for_pub(uint64_t sub_id, uint64_t pub_id,
bool use_take_shared_method);
458 const rclcpp::PublisherBase::SharedPtr & pub,
459 const rclcpp::experimental::SubscriptionIntraProcessBase::SharedPtr & sub)
const;
462 typename ROSMessageType,
463 typename Alloc = std::allocator<ROSMessageType>
465 void do_transient_local_publish(
466 const uint64_t pub_id,
const uint64_t sub_id,
467 const bool use_take_shared_method)
469 using ROSMessageTypeAllocatorTraits = allocator::AllocRebind<ROSMessageType, Alloc>;
470 using ROSMessageTypeAllocator =
typename ROSMessageTypeAllocatorTraits::allocator_type;
471 using ROSMessageTypeDeleter = allocator::Deleter<ROSMessageTypeAllocator, ROSMessageType>;
473 auto publisher_buffer = publisher_buffers_[pub_id].lock();
474 if (!publisher_buffer) {
475 throw std::runtime_error(
"publisher buffer has unexpectedly gone out of scope");
477 auto buffer = std::dynamic_pointer_cast<
480 ROSMessageTypeAllocator,
481 ROSMessageTypeDeleter
485 throw std::runtime_error(
486 "failed to dynamic cast publisher's IntraProcessBufferBase to "
487 "IntraProcessBuffer<ROSMessageType,ROSMessageTypeAllocator,"
488 "ROSMessageTypeDeleter> which can happen when the publisher and "
489 "subscription use different allocator types, which is not supported");
491 if (use_take_shared_method) {
492 auto data_vec = buffer->get_all_data_shared();
493 for (
auto shared_data : data_vec) {
494 this->
template add_shared_msg_to_buffer<
495 ROSMessageType, ROSMessageTypeAllocator, ROSMessageTypeDeleter, ROSMessageType>(
496 shared_data, sub_id);
499 auto data_vec = buffer->get_all_data_unique();
500 for (
auto & owned_data : data_vec) {
501 auto allocator = ROSMessageTypeAllocator();
502 this->
template add_owned_msg_to_buffer<
503 ROSMessageType, ROSMessageTypeAllocator, ROSMessageTypeDeleter, ROSMessageType>(
504 std::move(owned_data), sub_id, allocator);
513 typename ROSMessageType>
515 add_shared_msg_to_buffers(
516 std::shared_ptr<const MessageT> message,
517 std::vector<uint64_t> subscription_ids)
519 using ROSMessageTypeAllocatorTraits = allocator::AllocRebind<ROSMessageType, Alloc>;
520 using ROSMessageTypeAllocator =
typename ROSMessageTypeAllocatorTraits::allocator_type;
521 using ROSMessageTypeDeleter = allocator::Deleter<ROSMessageTypeAllocator, ROSMessageType>;
523 using PublishedType =
typename rclcpp::TypeAdapter<MessageT>::custom_type;
524 using PublishedTypeAllocatorTraits = allocator::AllocRebind<PublishedType, Alloc>;
525 using PublishedTypeAllocator =
typename PublishedTypeAllocatorTraits::allocator_type;
526 using PublishedTypeDeleter = allocator::Deleter<PublishedTypeAllocator, PublishedType>;
528 for (
auto id : subscription_ids) {
529 auto subscription_it = subscriptions_.find(
id);
530 if (subscription_it == subscriptions_.end()) {
531 throw std::runtime_error(
"subscription has unexpectedly gone out of scope");
533 auto subscription_base = subscription_it->second.lock();
534 if (subscription_base ==
nullptr) {
535 subscriptions_.erase(
id);
539 auto subscription = std::dynamic_pointer_cast<
541 PublishedTypeAllocator, PublishedTypeDeleter, ROSMessageType>
542 >(subscription_base);
543 if (subscription !=
nullptr) {
544 subscription->provide_intra_process_data(message);
548 auto ros_message_subscription = std::dynamic_pointer_cast<
550 ROSMessageTypeAllocator, ROSMessageTypeDeleter>
551 >(subscription_base);
552 if (
nullptr == ros_message_subscription) {
553 throw std::runtime_error(
554 "failed to dynamic cast SubscriptionIntraProcessBase to "
555 "SubscriptionIntraProcessBuffer<MessageT, Alloc, Deleter>, or to "
556 "SubscriptionROSMsgIntraProcessBuffer<ROSMessageType,ROSMessageTypeAllocator,"
557 "ROSMessageTypeDeleter> which can happen when the publisher and "
558 "subscription use different allocator types, which is not supported");
562 ROSMessageType ros_msg;
564 ros_message_subscription->provide_intra_process_message(
565 std::make_shared<ROSMessageType>(ros_msg));
567 if constexpr (std::is_same<MessageT, ROSMessageType>::value) {
568 ros_message_subscription->provide_intra_process_message(message);
571 ROSMessageType>::ros_message_type, ROSMessageType>::value)
573 ROSMessageType ros_msg;
576 ros_message_subscription->provide_intra_process_message(
577 std::make_shared<ROSMessageType>(ros_msg));
588 typename ROSMessageType>
590 add_owned_msg_to_buffers(
591 std::unique_ptr<MessageT, Deleter> message,
592 std::vector<uint64_t> subscription_ids,
593 typename allocator::AllocRebind<MessageT, Alloc>::allocator_type & allocator)
595 using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
596 using MessageUniquePtr = std::unique_ptr<MessageT, Deleter>;
598 using ROSMessageTypeAllocatorTraits = allocator::AllocRebind<ROSMessageType, Alloc>;
599 using ROSMessageTypeAllocator =
typename ROSMessageTypeAllocatorTraits::allocator_type;
600 using ROSMessageTypeDeleter = allocator::Deleter<ROSMessageTypeAllocator, ROSMessageType>;
602 using PublishedType =
typename rclcpp::TypeAdapter<MessageT>::custom_type;
603 using PublishedTypeAllocatorTraits = allocator::AllocRebind<PublishedType, Alloc>;
604 using PublishedTypeAllocator =
typename PublishedTypeAllocatorTraits::allocator_type;
605 using PublishedTypeDeleter = allocator::Deleter<PublishedTypeAllocator, PublishedType>;
607 for (
auto it = subscription_ids.begin(); it != subscription_ids.end(); it++) {
608 auto subscription_it = subscriptions_.find(*it);
609 if (subscription_it == subscriptions_.end()) {
610 throw std::runtime_error(
"subscription has unexpectedly gone out of scope");
612 auto subscription_base = subscription_it->second.lock();
613 if (subscription_base ==
nullptr) {
614 subscriptions_.erase(subscription_it);
618 auto subscription = std::dynamic_pointer_cast<
620 PublishedTypeAllocator, PublishedTypeDeleter, ROSMessageType>
621 >(subscription_base);
622 if (subscription !=
nullptr) {
623 if (std::next(it) == subscription_ids.end()) {
625 subscription->provide_intra_process_data(std::move(message));
630 Deleter deleter = message.get_deleter();
631 auto ptr = MessageAllocTraits::allocate(allocator, 1);
632 MessageAllocTraits::construct(allocator, ptr, *message);
634 subscription->provide_intra_process_data(MessageUniquePtr(ptr, deleter));
640 auto ros_message_subscription = std::dynamic_pointer_cast<
642 ROSMessageTypeAllocator, ROSMessageTypeDeleter>
643 >(subscription_base);
644 if (
nullptr == ros_message_subscription) {
645 throw std::runtime_error(
646 "failed to dynamic cast SubscriptionIntraProcessBase to "
647 "SubscriptionIntraProcessBuffer<MessageT, Alloc, Deleter>, or to "
648 "SubscriptionROSMsgIntraProcessBuffer<ROSMessageType,ROSMessageTypeAllocator,"
649 "ROSMessageTypeDeleter> which can happen when the publisher and "
650 "subscription use different allocator types, which is not supported");
654 ROSMessageTypeAllocator ros_message_alloc(allocator);
655 auto ptr = ROSMessageTypeAllocatorTraits::allocate(ros_message_alloc, 1);
656 ROSMessageTypeAllocatorTraits::construct(ros_message_alloc, ptr);
657 ROSMessageTypeDeleter deleter;
658 allocator::set_allocator_for_deleter(&deleter, &allocator);
660 auto ros_msg = std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter>(ptr, deleter);
661 ros_message_subscription->provide_intra_process_message(std::move(ros_msg));
663 if constexpr (std::is_same<MessageT, ROSMessageType>::value) {
664 if (std::next(it) == subscription_ids.end()) {
666 ros_message_subscription->provide_intra_process_message(std::move(message));
671 Deleter deleter = message.get_deleter();
672 allocator::set_allocator_for_deleter(&deleter, &allocator);
673 auto ptr = MessageAllocTraits::allocate(allocator, 1);
674 MessageAllocTraits::construct(allocator, ptr, *message);
676 ros_message_subscription->provide_intra_process_message(
677 MessageUniquePtr(ptr, deleter));
684 PublisherToSubscriptionIdsMap pub_to_subs_;
685 SubscriptionMap subscriptions_;
686 PublisherMap publishers_;
687 PublisherBufferMap publisher_buffers_;
689 mutable std::shared_timed_mutex mutex_;
691 GidToPublisherInfoMap gid_to_publisher_info_;
This class performs intra process communication between nodes.
RCLCPP_PUBLIC bool matches_any_publishers(const rmw_gid_t *id) const
Return true if the given rmw_gid_t matches any stored Publishers.
RCLCPP_PUBLIC void remove_subscription(uint64_t intra_process_subscription_id)
Unregister a subscription using the subscription's unique id.
uint64_t add_subscription(const rclcpp::experimental::SubscriptionIntraProcessBase::SharedPtr &subscription)
Register a subscription with the manager, returns subscriptions unique id.
void do_intra_process_publish(uint64_t intra_process_publisher_id, std::unique_ptr< MessageT, Deleter > message, typename allocator::AllocRebind< MessageT, Alloc >::allocator_type &allocator)
Publishes an intra-process message, passed as a unique pointer.
RCLCPP_PUBLIC void remove_publisher(uint64_t intra_process_publisher_id)
Unregister a publisher using the publisher's unique id.
RCLCPP_PUBLIC size_t get_subscription_count(uint64_t intra_process_publisher_id) const
Return the number of intraprocess subscriptions that are matched with a given publisher id.
RCLCPP_PUBLIC size_t lowest_available_capacity(const uint64_t intra_process_publisher_id) const
Return the lowest available capacity for all subscription buffers for a publisher id.
RCLCPP_PUBLIC uint64_t add_publisher(const rclcpp::PublisherBase::SharedPtr &publisher, const rclcpp::experimental::buffers::IntraProcessBufferBase::SharedPtr &buffer=rclcpp::experimental::buffers::IntraProcessBufferBase::SharedPtr())
Register a publisher with the manager, returns the publisher unique id.
Versions of rosidl_typesupport_cpp::get_message_type_support_handle that handle adapted types.
RCLCPP_PUBLIC Logger get_logger(const std::string &name)
Return a named logger.
Template structure used to adapt custom, user-defined types to ROS types.