3737
3838#include " rclcpp/clock.hpp"
3939#include " rclcpp/detail/cpp_callback_trampoline.hpp"
40+ #include " rclcpp/detail/resolve_use_intra_process.hpp"
4041#include " rclcpp/exceptions.hpp"
4142#include " rclcpp/expand_topic_or_service_name.hpp"
43+ #include " rclcpp/experimental/client_intra_process.hpp"
44+ #include " rclcpp/experimental/intra_process_manager.hpp"
4245#include " rclcpp/function_traits.hpp"
46+ #include " rclcpp/intra_process_setting.hpp"
4347#include " rclcpp/logging.hpp"
4448#include " rclcpp/macros.hpp"
4549#include " rclcpp/node_interfaces/node_graph_interface.hpp"
4852#include " rclcpp/utilities.hpp"
4953#include " rclcpp/visibility_control.hpp"
5054
55+ #include " rcutils/logging_macros.h"
56+
5157#include " rmw/error_handling.h"
5258#include " rmw/impl/cpp/demangle.hpp"
5359#include " rmw/rmw.h"
@@ -133,7 +139,7 @@ class ClientBase
133139 rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph);
134140
135141 RCLCPP_PUBLIC
136- virtual ~ClientBase () = default ;
142+ virtual ~ClientBase ();
137143
138144 // / Take the next response for this client as a type erased pointer.
139145 /* *
@@ -254,6 +260,15 @@ class ClientBase
254260 rclcpp::QoS
255261 get_response_subscription_actual_qos () const ;
256262
263+ // / Return the waitable for intra-process
264+ /* *
265+ * \return the waitable sharedpointer for intra-process, or nullptr if intra-process is not setup.
266+ * \throws std::runtime_error if the intra process manager is destroyed
267+ */
268+ RCLCPP_PUBLIC
269+ rclcpp::Waitable::SharedPtr
270+ get_intra_process_waitable ();
271+
257272 // / Set a callback to be called when each new response is received.
258273 /* *
259274 * The callback receives a size_t which is the number of responses received
@@ -358,6 +373,19 @@ class ClientBase
358373 void
359374 set_on_new_response_callback (rcl_event_callback_t callback, const void * user_data);
360375
376+ using IntraProcessManagerWeakPtr =
377+ std::weak_ptr<rclcpp::experimental::IntraProcessManager>;
378+
379+ // / Implementation detail.
380+ RCLCPP_PUBLIC
381+ void
382+ setup_intra_process (
383+ uint64_t intra_process_client_id,
384+ IntraProcessManagerWeakPtr weak_ipm);
385+
386+ std::shared_ptr<rclcpp::experimental::ClientIntraProcessBase> client_intra_process_;
387+ std::atomic_uint ipc_sequence_number_{1 };
388+
361389 rclcpp::node_interfaces::NodeGraphInterface::WeakPtr node_graph_;
362390 std::shared_ptr<rcl_node_t > node_handle_;
363391 std::shared_ptr<rclcpp::Context> context_;
@@ -373,6 +401,14 @@ class ClientBase
373401 std::shared_ptr<rcl_client_t > client_handle_;
374402
375403 std::atomic<bool > in_use_by_wait_set_{false };
404+
405+ std::recursive_mutex ipc_mutex_;
406+ bool use_intra_process_{false };
407+ IntraProcessManagerWeakPtr weak_ipm_;
408+ uint64_t intra_process_client_id_;
409+
410+ std::recursive_mutex callback_mutex_;
411+ std::function<void (size_t )> on_new_response_callback_{nullptr };
376412};
377413
378414template <typename ServiceT>
@@ -468,14 +504,16 @@ class Client : public ClientBase
468504 * \param[in] node_graph The node graph interface of the corresponding node.
469505 * \param[in] service_name Name of the topic to publish to.
470506 * \param[in] client_options options for the subscription.
507+ * \param[in] ipc_setting Intra-process communication setting for the client.
471508 */
472509 Client (
473510 rclcpp::node_interfaces::NodeBaseInterface * node_base,
474511 rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph,
475512 const std::string & service_name,
476- rcl_client_options_t & client_options)
513+ rcl_client_options_t & client_options,
514+ rclcpp::IntraProcessSetting ipc_setting = rclcpp::IntraProcessSetting::NodeDefault)
477515 : ClientBase(node_base, node_graph),
478- srv_type_support_handle_ (rosidl_typesupport_cpp::get_service_type_support_handle<ServiceT>())
516+ srv_type_support_handle_ (rosidl_typesupport_cpp::get_service_type_support_handle<ServiceT>())
479517 {
480518 rcl_ret_t ret = rcl_client_init (
481519 this ->get_client_handle ().get (),
@@ -496,10 +534,27 @@ class Client : public ClientBase
496534 }
497535 rclcpp::exceptions::throw_from_rcl_error (ret, " could not create client" );
498536 }
537+
538+ // Setup intra process if requested.
539+ if (rclcpp::detail::resolve_use_intra_process (ipc_setting, *node_base)) {
540+ create_intra_process_client ();
541+ }
499542 }
500543
501544 virtual ~Client ()
502545 {
546+ if (!use_intra_process_) {
547+ return ;
548+ }
549+ auto ipm = weak_ipm_.lock ();
550+ if (!ipm) {
551+ // TODO(ivanpauno): should this raise an error?
552+ RCLCPP_WARN (
553+ rclcpp::get_logger (" rclcpp" ),
554+ " Intra process manager died before than a client." );
555+ return ;
556+ }
557+ ipm->remove_client (intra_process_client_id_);
503558 }
504559
505560 // / Take the next response for this client.
@@ -616,7 +671,7 @@ class Client : public ClientBase
616671 Promise promise;
617672 auto future = promise.get_future ();
618673 auto req_id = async_send_request_impl (
619- * request,
674+ std::move ( request) ,
620675 std::move (promise));
621676 return FutureAndRequestId (std::move (future), req_id);
622677 }
@@ -651,7 +706,7 @@ class Client : public ClientBase
651706 Promise promise;
652707 auto shared_future = promise.get_future ().share ();
653708 auto req_id = async_send_request_impl (
654- * request,
709+ std::move ( request) ,
655710 std::make_tuple (
656711 CallbackType{std::forward<CallbackT>(cb)},
657712 shared_future,
@@ -682,7 +737,7 @@ class Client : public ClientBase
682737 PromiseWithRequest promise;
683738 auto shared_future = promise.get_future ().share ();
684739 auto req_id = async_send_request_impl (
685- * request,
740+ request,
686741 std::make_tuple (
687742 CallbackWithRequestType{std::forward<CallbackT>(cb)},
688743 request,
@@ -824,8 +879,30 @@ class Client : public ClientBase
824879 CallbackWithRequestTypeValueVariant>;
825880
826881 int64_t
827- async_send_request_impl (const Request & request, CallbackInfoVariant value)
882+ async_send_request_impl (SharedRequest request, CallbackInfoVariant value)
828883 {
884+ std::lock_guard<std::recursive_mutex> lock (ipc_mutex_);
885+ if (use_intra_process_) {
886+ auto ipm = weak_ipm_.lock ();
887+ if (!ipm) {
888+ throw std::runtime_error (
889+ " intra process send called after destruction of intra process manager" );
890+ }
891+ bool intra_process_server_available = ipm->service_is_available (intra_process_client_id_);
892+
893+ // Check if there's an intra-process server available matching this client.
894+ // If there's not, we fall back into inter-process communication, since
895+ // the server might be available in another process or was configured to not use IPC.
896+ if (intra_process_server_available) {
897+ // Send intra-process request
898+ ipm->send_intra_process_client_request <ServiceT>(
899+ intra_process_client_id_,
900+ std::make_pair (std::move (request), std::move (value)));
901+ return ipc_sequence_number_++;
902+ }
903+ }
904+
905+ // Send inter-process request
829906 int64_t sequence_number;
830907 std::lock_guard<std::mutex> lock (pending_requests_mutex_);
831908 rcl_ret_t ret = rcl_send_request (get_client_handle ().get (), &request, &sequence_number);
@@ -854,6 +931,40 @@ class Client : public ClientBase
854931 return value;
855932 }
856933
934+ void
935+ create_intra_process_client ()
936+ {
937+ // Check if the QoS is compatible with intra-process.
938+ auto qos_profile = get_response_subscription_actual_qos ();
939+
940+ if (qos_profile.history () != rclcpp::HistoryPolicy::KeepLast) {
941+ throw std::invalid_argument (
942+ " intraprocess communication allowed only with keep last history qos policy" );
943+ }
944+ if (qos_profile.depth () == 0 ) {
945+ throw std::invalid_argument (
946+ " intraprocess communication is not allowed with 0 depth qos policy" );
947+ }
948+ if (qos_profile.durability () != rclcpp::DurabilityPolicy::Volatile) {
949+ throw std::invalid_argument (
950+ " intraprocess communication allowed only with volatile durability" );
951+ }
952+
953+ // Create a ClientIntraProcess which will be given to the intra-process manager.
954+ using ClientIntraProcessT = rclcpp::experimental::ClientIntraProcess<ServiceT>;
955+
956+ client_intra_process_ = std::make_shared<ClientIntraProcessT>(
957+ context_,
958+ this ->get_service_name (),
959+ qos_profile);
960+
961+ // Add it to the intra process manager.
962+ using rclcpp::experimental::IntraProcessManager;
963+ auto ipm = context_->get_sub_context <IntraProcessManager>();
964+ uint64_t intra_process_client_id = ipm->add_intra_process_client (client_intra_process_);
965+ this ->setup_intra_process (intra_process_client_id, ipm);
966+ }
967+
857968 RCLCPP_DISABLE_COPY (Client)
858969
859970 std::unordered_map<
0 commit comments