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_base.hpp"
35 #include "rclcpp/experimental/subscription_intra_process_buffer.hpp"
36 #include "rclcpp/logger.hpp"
37 #include "rclcpp/logging.hpp"
38 #include "rclcpp/macros.hpp"
39 #include "rclcpp/publisher_base.hpp"
40 #include "rclcpp/type_adapter.hpp"
41 #include "rclcpp/visibility_control.hpp"
46 namespace experimental
117 typename ROSMessageType,
118 typename Alloc = std::allocator<ROSMessageType>
122 const rclcpp::experimental::SubscriptionIntraProcessBase::SharedPtr & subscription)
124 std::unique_lock<std::shared_timed_mutex> lock(mutex_);
126 uint64_t sub_id = IntraProcessManager::get_next_unique_id();
128 subscriptions_[sub_id] = subscription;
131 for (
auto & pair : publishers_) {
132 auto publisher = pair.second.lock();
136 if (can_communicate(publisher, subscription)) {
137 uint64_t pub_id = pair.first;
138 insert_sub_id_for_pub(sub_id, pub_id, subscription->use_take_shared_method());
139 if (publisher->is_durability_transient_local() &&
140 subscription->is_durability_transient_local())
142 do_transient_local_publish<ROSMessageType, Alloc>(
144 subscription->use_take_shared_method());
179 const rclcpp::PublisherBase::SharedPtr & publisher,
180 const rclcpp::experimental::buffers::IntraProcessBufferBase::SharedPtr & buffer =
181 rclcpp::experimental::buffers::IntraProcessBufferBase::SharedPtr());
219 typename ROSMessageType,
221 typename Deleter = std::default_delete<MessageT>
225 uint64_t intra_process_publisher_id,
226 std::unique_ptr<MessageT, Deleter> message,
227 typename allocator::AllocRebind<MessageT, Alloc>::allocator_type & allocator)
229 using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
230 using MessageAllocatorT =
typename MessageAllocTraits::allocator_type;
232 std::shared_lock<std::shared_timed_mutex> lock(mutex_);
234 auto publisher_it = pub_to_subs_.find(intra_process_publisher_id);
235 if (publisher_it == pub_to_subs_.end()) {
239 "Calling do_intra_process_publish for invalid or no longer existing publisher id");
242 const auto & sub_ids = publisher_it->second;
244 if (sub_ids.take_ownership_subscriptions.empty()) {
246 std::shared_ptr<MessageT> msg = std::move(message);
248 this->
template add_shared_msg_to_buffers<MessageT, Alloc, Deleter, ROSMessageType>(
249 msg, sub_ids.take_shared_subscriptions);
250 }
else if (!sub_ids.take_ownership_subscriptions.empty() &&
251 sub_ids.take_shared_subscriptions.size() <= 1)
257 std::vector<uint64_t> concatenated_vector(
258 sub_ids.take_shared_subscriptions.begin(), sub_ids.take_shared_subscriptions.end());
259 concatenated_vector.insert(
260 concatenated_vector.end(),
261 sub_ids.take_ownership_subscriptions.begin(),
262 sub_ids.take_ownership_subscriptions.end());
263 this->
template add_owned_msg_to_buffers<MessageT, Alloc, Deleter, ROSMessageType>(
267 }
else if (!sub_ids.take_ownership_subscriptions.empty() &&
268 sub_ids.take_shared_subscriptions.size() > 1)
272 auto shared_msg = std::allocate_shared<MessageT, MessageAllocatorT>(allocator, *message);
274 this->
template add_shared_msg_to_buffers<MessageT, Alloc, Deleter, ROSMessageType>(
275 shared_msg, sub_ids.take_shared_subscriptions);
276 this->
template add_owned_msg_to_buffers<MessageT, Alloc, Deleter, ROSMessageType>(
277 std::move(message), sub_ids.take_ownership_subscriptions, allocator);
283 typename ROSMessageType,
285 typename Deleter = std::default_delete<MessageT>
287 std::shared_ptr<const MessageT>
288 do_intra_process_publish_and_return_shared(
289 uint64_t intra_process_publisher_id,
290 std::unique_ptr<MessageT, Deleter> message,
291 typename allocator::AllocRebind<MessageT, Alloc>::allocator_type & allocator)
293 using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
294 using MessageAllocatorT =
typename MessageAllocTraits::allocator_type;
296 std::shared_lock<std::shared_timed_mutex> lock(mutex_);
298 auto publisher_it = pub_to_subs_.find(intra_process_publisher_id);
299 if (publisher_it == pub_to_subs_.end()) {
303 "Calling do_intra_process_publish for invalid or no longer existing publisher id");
306 const auto & sub_ids = publisher_it->second;
308 if (sub_ids.take_ownership_subscriptions.empty()) {
310 std::shared_ptr<MessageT> shared_msg = std::move(message);
311 if (!sub_ids.take_shared_subscriptions.empty()) {
312 this->
template add_shared_msg_to_buffers<MessageT, Alloc, Deleter, ROSMessageType>(
313 shared_msg, sub_ids.take_shared_subscriptions);
319 auto shared_msg = std::allocate_shared<MessageT, MessageAllocatorT>(allocator, *message);
321 if (!sub_ids.take_shared_subscriptions.empty()) {
322 this->
template add_shared_msg_to_buffers<MessageT, Alloc, Deleter, ROSMessageType>(
324 sub_ids.take_shared_subscriptions);
326 if (!sub_ids.take_ownership_subscriptions.empty()) {
327 this->
template add_owned_msg_to_buffers<MessageT, Alloc, Deleter, ROSMessageType>(
329 sub_ids.take_ownership_subscriptions,
340 typename ROSMessageType>
342 add_shared_msg_to_buffer(
343 std::shared_ptr<const MessageT> message,
344 uint64_t subscription_id)
346 add_shared_msg_to_buffers<MessageT, Alloc, Deleter, ROSMessageType>(message, {subscription_id});
353 typename ROSMessageType>
355 add_owned_msg_to_buffer(
356 std::unique_ptr<MessageT, Deleter> message,
357 uint64_t subscription_id,
358 typename allocator::AllocRebind<MessageT, Alloc>::allocator_type & allocator)
360 add_owned_msg_to_buffers<MessageT, Alloc, Deleter, ROSMessageType>(
361 std::move(message), {subscription_id}, allocator);
375 rclcpp::experimental::SubscriptionIntraProcessBase::SharedPtr
376 get_subscription_intra_process(uint64_t intra_process_subscription_id);
384 struct SplittedSubscriptions
386 std::vector<uint64_t> take_shared_subscriptions;
387 std::vector<uint64_t> take_ownership_subscriptions;
393 std::size_t operator()(
const rmw_gid_t & gid)
const noexcept
396 constexpr std::size_t FNV_prime = 1099511628211u;
397 std::size_t result = 14695981039346656037u;
399 for (std::size_t i = 0; i < RMW_GID_STORAGE_SIZE; ++i) {
400 result ^= gid.data[i];
410 bool operator()(
const rmw_gid_t & lhs,
const rmw_gid_t & rhs)
const noexcept
417 std::begin(lhs.data),
419 std::begin(rhs.data));
423 using SubscriptionMap =
424 std::unordered_map<uint64_t, rclcpp::experimental::SubscriptionIntraProcessBase::WeakPtr>;
427 std::unordered_map<uint64_t, rclcpp::PublisherBase::WeakPtr>;
429 using PublisherBufferMap =
430 std::unordered_map<uint64_t, rclcpp::experimental::buffers::IntraProcessBufferBase::WeakPtr>;
432 using PublisherToSubscriptionIdsMap =
433 std::unordered_map<uint64_t, SplittedSubscriptions>;
439 rclcpp::PublisherBase::WeakPtr publisher;
442 using GidToPublisherInfoMap =
443 std::unordered_map<rmw_gid_t, PublisherInfo, rmw_gid_hash, rmw_gid_equal>;
448 get_next_unique_id();
452 insert_sub_id_for_pub(uint64_t sub_id, uint64_t pub_id,
bool use_take_shared_method);
457 const rclcpp::PublisherBase::SharedPtr & pub,
458 const rclcpp::experimental::SubscriptionIntraProcessBase::SharedPtr & sub)
const;
461 typename ROSMessageType,
462 typename Alloc = std::allocator<ROSMessageType>
464 void do_transient_local_publish(
465 const uint64_t pub_id,
const uint64_t sub_id,
466 const bool use_take_shared_method)
468 using ROSMessageTypeAllocatorTraits = allocator::AllocRebind<ROSMessageType, Alloc>;
469 using ROSMessageTypeAllocator =
typename ROSMessageTypeAllocatorTraits::allocator_type;
470 using ROSMessageTypeDeleter = allocator::Deleter<ROSMessageTypeAllocator, ROSMessageType>;
472 auto publisher_buffer = publisher_buffers_[pub_id].lock();
473 if (!publisher_buffer) {
474 throw std::runtime_error(
"publisher buffer has unexpectedly gone out of scope");
476 auto buffer = std::dynamic_pointer_cast<
479 ROSMessageTypeAllocator,
480 ROSMessageTypeDeleter
484 throw std::runtime_error(
485 "failed to dynamic cast publisher's IntraProcessBufferBase to "
486 "IntraProcessBuffer<ROSMessageType,ROSMessageTypeAllocator,"
487 "ROSMessageTypeDeleter> which can happen when the publisher and "
488 "subscription use different allocator types, which is not supported");
490 if (use_take_shared_method) {
491 auto data_vec = buffer->get_all_data_shared();
492 for (
auto shared_data : data_vec) {
493 this->
template add_shared_msg_to_buffer<
494 ROSMessageType, ROSMessageTypeAllocator, ROSMessageTypeDeleter, ROSMessageType>(
495 shared_data, sub_id);
498 auto data_vec = buffer->get_all_data_unique();
499 for (
auto & owned_data : data_vec) {
500 auto allocator = ROSMessageTypeAllocator();
501 this->
template add_owned_msg_to_buffer<
502 ROSMessageType, ROSMessageTypeAllocator, ROSMessageTypeDeleter, ROSMessageType>(
503 std::move(owned_data), sub_id, allocator);
512 typename ROSMessageType>
514 add_shared_msg_to_buffers(
515 std::shared_ptr<const MessageT> message,
516 std::vector<uint64_t> subscription_ids)
518 using ROSMessageTypeAllocatorTraits = allocator::AllocRebind<ROSMessageType, Alloc>;
519 using ROSMessageTypeAllocator =
typename ROSMessageTypeAllocatorTraits::allocator_type;
520 using ROSMessageTypeDeleter = allocator::Deleter<ROSMessageTypeAllocator, ROSMessageType>;
522 using PublishedType =
typename rclcpp::TypeAdapter<MessageT>::custom_type;
523 using PublishedTypeAllocatorTraits = allocator::AllocRebind<PublishedType, Alloc>;
524 using PublishedTypeAllocator =
typename PublishedTypeAllocatorTraits::allocator_type;
525 using PublishedTypeDeleter = allocator::Deleter<PublishedTypeAllocator, PublishedType>;
527 for (
auto id : subscription_ids) {
528 auto subscription_it = subscriptions_.find(
id);
529 if (subscription_it == subscriptions_.end()) {
530 throw std::runtime_error(
"subscription has unexpectedly gone out of scope");
532 auto subscription_base = subscription_it->second.lock();
533 if (subscription_base ==
nullptr) {
534 subscriptions_.erase(
id);
538 auto subscription = std::dynamic_pointer_cast<
540 PublishedTypeAllocator, PublishedTypeDeleter, ROSMessageType>
541 >(subscription_base);
542 if (subscription !=
nullptr) {
543 subscription->provide_intra_process_data(message);
547 auto ros_message_subscription = std::dynamic_pointer_cast<
549 ROSMessageTypeAllocator, ROSMessageTypeDeleter>
550 >(subscription_base);
551 if (
nullptr == ros_message_subscription) {
552 throw std::runtime_error(
553 "failed to dynamic cast SubscriptionIntraProcessBase to "
554 "SubscriptionIntraProcessBuffer<MessageT, Alloc, Deleter>, or to "
555 "SubscriptionROSMsgIntraProcessBuffer<ROSMessageType,ROSMessageTypeAllocator,"
556 "ROSMessageTypeDeleter> which can happen when the publisher and "
557 "subscription use different allocator types, which is not supported");
561 ROSMessageType ros_msg;
563 ros_message_subscription->provide_intra_process_message(
564 std::make_shared<ROSMessageType>(ros_msg));
566 if constexpr (std::is_same<MessageT, ROSMessageType>::value) {
567 ros_message_subscription->provide_intra_process_message(message);
570 ROSMessageType>::ros_message_type, ROSMessageType>::value)
572 ROSMessageType ros_msg;
575 ros_message_subscription->provide_intra_process_message(
576 std::make_shared<ROSMessageType>(ros_msg));
587 typename ROSMessageType>
589 add_owned_msg_to_buffers(
590 std::unique_ptr<MessageT, Deleter> message,
591 std::vector<uint64_t> subscription_ids,
592 typename allocator::AllocRebind<MessageT, Alloc>::allocator_type & allocator)
594 using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
595 using MessageUniquePtr = std::unique_ptr<MessageT, Deleter>;
597 using ROSMessageTypeAllocatorTraits = allocator::AllocRebind<ROSMessageType, Alloc>;
598 using ROSMessageTypeAllocator =
typename ROSMessageTypeAllocatorTraits::allocator_type;
599 using ROSMessageTypeDeleter = allocator::Deleter<ROSMessageTypeAllocator, ROSMessageType>;
601 using PublishedType =
typename rclcpp::TypeAdapter<MessageT>::custom_type;
602 using PublishedTypeAllocatorTraits = allocator::AllocRebind<PublishedType, Alloc>;
603 using PublishedTypeAllocator =
typename PublishedTypeAllocatorTraits::allocator_type;
604 using PublishedTypeDeleter = allocator::Deleter<PublishedTypeAllocator, PublishedType>;
606 for (
auto it = subscription_ids.begin(); it != subscription_ids.end(); it++) {
607 auto subscription_it = subscriptions_.find(*it);
608 if (subscription_it == subscriptions_.end()) {
609 throw std::runtime_error(
"subscription has unexpectedly gone out of scope");
611 auto subscription_base = subscription_it->second.lock();
612 if (subscription_base ==
nullptr) {
613 subscriptions_.erase(subscription_it);
617 auto subscription = std::dynamic_pointer_cast<
619 PublishedTypeAllocator, PublishedTypeDeleter, ROSMessageType>
620 >(subscription_base);
621 if (subscription !=
nullptr) {
622 if (std::next(it) == subscription_ids.end()) {
624 subscription->provide_intra_process_data(std::move(message));
629 Deleter deleter = message.get_deleter();
630 auto ptr = MessageAllocTraits::allocate(allocator, 1);
631 MessageAllocTraits::construct(allocator, ptr, *message);
633 subscription->provide_intra_process_data(MessageUniquePtr(ptr, deleter));
639 auto ros_message_subscription = std::dynamic_pointer_cast<
641 ROSMessageTypeAllocator, ROSMessageTypeDeleter>
642 >(subscription_base);
643 if (
nullptr == ros_message_subscription) {
644 throw std::runtime_error(
645 "failed to dynamic cast SubscriptionIntraProcessBase to "
646 "SubscriptionIntraProcessBuffer<MessageT, Alloc, Deleter>, or to "
647 "SubscriptionROSMsgIntraProcessBuffer<ROSMessageType,ROSMessageTypeAllocator,"
648 "ROSMessageTypeDeleter> which can happen when the publisher and "
649 "subscription use different allocator types, which is not supported");
653 ROSMessageTypeAllocator ros_message_alloc(allocator);
654 auto ptr = ROSMessageTypeAllocatorTraits::allocate(ros_message_alloc, 1);
655 ROSMessageTypeAllocatorTraits::construct(ros_message_alloc, ptr);
656 ROSMessageTypeDeleter deleter;
657 allocator::set_allocator_for_deleter(&deleter, &allocator);
659 auto ros_msg = std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter>(ptr, deleter);
660 ros_message_subscription->provide_intra_process_message(std::move(ros_msg));
662 if constexpr (std::is_same<MessageT, ROSMessageType>::value) {
663 if (std::next(it) == subscription_ids.end()) {
665 ros_message_subscription->provide_intra_process_message(std::move(message));
670 Deleter deleter = message.get_deleter();
671 allocator::set_allocator_for_deleter(&deleter, &allocator);
672 auto ptr = MessageAllocTraits::allocate(allocator, 1);
673 MessageAllocTraits::construct(allocator, ptr, *message);
675 ros_message_subscription->provide_intra_process_message(
676 MessageUniquePtr(ptr, deleter));
683 PublisherToSubscriptionIdsMap pub_to_subs_;
684 SubscriptionMap subscriptions_;
685 PublisherMap publishers_;
686 PublisherBufferMap publisher_buffers_;
688 mutable std::shared_timed_mutex mutex_;
690 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.