25#include <flow/log/log.hpp>
26#include <boost/make_shared.hpp>
27#include <boost/shared_ptr.hpp>
28#include <boost/thread/future.hpp>
166template<
typename Client_session_t>
168 public flow::log::Log_context,
169 private boost::noncopyable
176 static_assert(Session_obj::S_SHM_ENABLED,
177 "Client_context facilitates the use of Flow-IPC zero-copy (SHM) facilities for "
178 "zero-copy performance in using capnp-RPC; therefore the supplied Client_session_t template "
179 "parameter must be a SHM-enabled Client_session variant, session::shm::*::Client_session*.");
180 static_assert(Session_obj::Channel_obj::S_HAS_NATIVE_HANDLE_PIPE,
181 "Client_context, per its protocol with the opposing Context_server, sends a native handle -- "
182 "the stream-socket underlying the capnp-RPC conversation -- over the first init-channel; "
183 "therefore the supplied Client_session_t must be configured to emit Native_handle-bearing "
184 "channels (e.g., see aliases in session::shm::*::rpc namespaces).");
225 explicit Client_context(flow::log::Logger* logger_ptr, kj::AsyncIoContext* kj_io,
227 bool enable_hndl_transport =
true);
325 bool sync_connect(
typename Session_obj::Channels* init_channels_by_cli_req_pre_sized =
nullptr,
326 typename Session_obj::Channels* init_channels_by_srv_req =
nullptr);
346 typename Session_obj::Channels* init_channels_by_srv_req =
nullptr);
390 using Session_ptr =
typename Vat_network::Shm_lender_borrower_ptr;
424 typename Session_obj::Channels* init_channels_by_srv_req,
425 bool sans_shm_transport);
473template<
typename Client_session_t>
475 kj::AsyncIoContext* kj_io,
478 bool enable_hndl_transport) :
481 m_cli_app_ref(cli_app_ref),
482 m_srv_app_ref(srv_app_ref),
483 m_enable_hndl_transport(enable_hndl_transport),
484 m_session(boost::make_shared<
Session_obj>(get_logger(), m_cli_app_ref, m_srv_app_ref,
485 [logger_ptr, &cli_app_ref, &srv_app_ref](const
Error_code& err_code)
488 FLOW_LOG_INFO(
"rpc::Client_ctx [" << *
this <<
"]: "
489 "Created in NULL state (inactive). Inactive ipc::session::Session [" << *m_session <<
"] exists.");
492template<
typename Client_session_t>
495 FLOW_LOG_INFO(
"rpc::Client_ctx [" << *
this <<
"]: "
496 "Shutting down. Session_vat_network shall shut down; then the ipc::session::Session "
497 "(unless a straggling capnp-RPC message keeps the latter alive a bit longer).");
500template<
typename Client_session_t>
506 FLOW_LOG_SET_CONTEXT(logger_ptr, Log_component::S_RPC);
507 FLOW_LOG_INFO(
"rpc::Client_ctx-created ipc::session::Session [" << cli_app.
m_name <<
"->" << srv_app.
m_name <<
"]: "
508 "session-hosed handler fired (code [" << err_code <<
"] [" << err_code.message() <<
"]). "
509 "This is likely normal, as the opposing process decided to end session; the RPC-system will "
510 "have detected same, and user RPC session should be ended or ending imminently, at which point "
511 "proper user code shall shut-down this Client_ctx which will shut down the Session_vat_network and "
512 "lastly the ipc::session::Session.");
538template<
typename Client_session_t>
540 (
typename Session_obj::Channels* init_channels_by_cli_req_pre_sized,
541 typename Session_obj::Channels* init_channels_by_srv_req)
543 return sync_connect_impl(init_channels_by_cli_req_pre_sized, init_channels_by_srv_req,
false);
546template<
typename Client_session_t>
548 (
typename Session_obj::Channels* init_channels_by_cli_req_pre_sized,
549 typename Session_obj::Channels* init_channels_by_srv_req)
551 return sync_connect_impl(init_channels_by_cli_req_pre_sized, init_channels_by_srv_req,
true);
554template<
typename Client_session_t>
556 (
typename Session_obj::Channels* init_channels_by_cli_req_pre_sized,
557 typename Session_obj::Channels* init_channels_by_srv_req,
558 bool sans_shm_transport)
560 using Channels =
typename Session_obj::Channels;
562 namespace asio_local = asio_local_stream_socket::local_ns;
563 using asio_local::connect_pair;
565 using flow::util::Task_engine;
566 using flow::error::Runtime_error;
567 using boost::promise;
569 using std::exception;
573 FLOW_LOG_WARNING(
"rpc::Client_ctx [" << *
this <<
"]: capnp-RPC connect requested, but we are already connected. "
582 Channels actual_init_channels_by_cli{1 + (init_channels_by_cli_req_pre_sized
583 ? init_channels_by_cli_req_pre_sized->size()
585 FLOW_LOG_INFO(
"rpc::Client_ctx [" << *
this <<
"]: capnp-RPC connect requested. Synchronous, non-blocking "
586 "ipc::session::Session connect initiating; on success will hook up the "
587 "[zero-copy-enabled? = [" << (!sans_shm_transport) <<
"]] Session_vat_network.");
596 m_session->sync_connect(m_session->mdt_builder(), &actual_init_channels_by_cli,
nullptr,
597 init_channels_by_srv_req);
598 assert(ok &&
"We had a NULL-state Client_session and tried .sync_connect(); why would it have reported otherwise?");
603 if (init_channels_by_cli_req_pre_sized && (!init_channels_by_cli_req_pre_sized->empty()))
605 assert(actual_init_channels_by_cli.size() > 1);
606 std::move(++actual_init_channels_by_cli.begin(), actual_init_channels_by_cli.end(),
607 init_channels_by_cli_req_pre_sized->begin());
608 actual_init_channels_by_cli.resize(1);
610 assert(actual_init_channels_by_cli.size() == 1);
611 auto& rpc_setup_channel = actual_init_channels_by_cli.front();
636 Task_engine task_engine;
640 connect_pair(local_sock, remote_sock, sys_err_code);
646 throw Runtime_error{sys_err_code,
"Client_context::sync_connect_impl(): connect_pair()"};
656 m_rpc_setup_channel.emplace(rpc_setup_channel.async_io_obj());
658 uint8_t protocol_dummy_payload = 0;
659 m_rpc_setup_channel->hndl_snd()->send_native_handle(remote_hndl,
661 sizeof(protocol_dummy_payload)});
669 m_rpc_setup_channel->hndl_snd()->end_sending();
736 if (m_enable_hndl_transport)
739 m_network.emplace(get_logger(), m_kj_io,
741 std::move(local_hndl));
746 m_network.emplace(get_logger(), m_kj_io,
748 std::move(local_hndl), 0);
750 assert(local_hndl.
null() &&
"Session_vat_network ctor promises to consume the transport handle immediately.");
762 catch (
const exception& exc) { msg = exc.what(); }
763 catch (
const kj::Exception& exc) { msg = exc.getDescription().cStr(); }
764 catch (...) { msg =
"(unknown exception type)"; }
766 FLOW_LOG_WARNING(
"rpc::Client_ctx [" << *
this <<
"]: A step of capnp-RPC connect (the ipc::session::Session "
767 "[" << *m_session <<
"] connect itself; socket-pair creation; socket-handle transmission; "
768 "Session_vat_network ctor) threw exception [" << msg <<
"] which we shall re-throw; "
769 "getting `*this` and out-args back to pristine state first.");
775 m_rpc_setup_channel.reset();
780 if (init_channels_by_cli_req_pre_sized)
782 *init_channels_by_cli_req_pre_sized = Channels{init_channels_by_cli_req_pre_sized->size()};
784 if (init_channels_by_srv_req)
786 init_channels_by_srv_req->clear();
792 = boost::make_shared<Session_obj>(get_logger(), m_cli_app_ref, m_srv_app_ref,
793 [logger_ptr = get_logger(), &cli_app = m_cli_app_ref, &srv_app = m_srv_app_ref]
795 { on_session_hosed(logger_ptr, cli_app, srv_app, err_code); });
796 assert((!m_network) &&
"If Session_vat_network ctor (last step) threw -- m_network should still be null! Bug?");
805template<
typename Client_session_t>
809 return m_network ? &(*m_network) :
nullptr;
812template<
typename Client_session_t>
819template<
typename Client_session_t>
823 return m_network ? m_session.get() :
nullptr;
826template<
typename Client_session_t>
833template<
typename Client_session_t>
840 os <<
"session[" << *(val.
session()) <<
"] vat_netwk[" << *network <<
']';
846 return os <<
"]@" << &val;
An object of this type facilitates the establishment, in client style, of a zero-copy-enabled capnp-R...
Session_obj * session()
Returns pointer to Session_obj, if sync_connect() has succeeded; or null if no sync_connect() has suc...
const Session_obj * session() const
Immutable counterpart to the other overload.
bool sync_connect(typename Session_obj::Channels *init_channels_by_cli_req_pre_sized=nullptr, typename Session_obj::Channels *init_channels_by_srv_req=nullptr)
To be invoked successfully at most once, it synchronously and non-blockingly attempts to connect to a...
typename Vat_network::Shm_lender_borrower_ptr Session_ptr
Basically shared_ptr<Session_obj>.
Session_ptr m_session
Session in NULL state (until sync_connect() succeeds) or PEER state (subsequently).
const bool m_enable_hndl_transport
See ctor.
bool sync_connect_sans_shm_transport(typename Session_obj::Channels *init_channels_by_cli_req_pre_sized=nullptr, typename Session_obj::Channels *init_channels_by_srv_req=nullptr)
Identical to sync_connect() with same signature but disables zero-copy transport.
const Vat_network * vat_network() const
Immutable counterpart to the other overload.
const session::Server_app & m_srv_app_ref
See ctor. Stored in case we must re-initialize m_session, if sync_connect() only partially succeeds.
std::optional< Vat_network > m_network
Null (until sync_connect() succeeds) or the established Vat_network (subsequently).
static void on_session_hosed(flow::log::Logger *logger_ptr, const session::Client_app &cli_app, const session::Server_app &srv_app, const Error_code &err_code)
Session-hosed handler for m_session.
Vat_network * vat_network()
Returns pointer to Session_vat_network established for use in your rpc::Rpc_system; or null if no syn...
Client_session_t Session_obj
Convenience alias for template arg.
typename Session_obj::Vat_network Vat_network
The Session_vat_network concrete type – and capnp::VatNetwork interface impl – that we produce.
std::optional< typename Session_obj::Channel_obj::Async_io_obj > m_rpc_setup_channel
Null until sync_connect() succeeds; then the idle channel being kept up, having been used to convey t...
kj::AsyncIoContext *const m_kj_io
KJ event loop context.
const session::Client_app & m_cli_app_ref
See ctor. Stored in case we must re-initialize m_session, if sync_connect() only partially succeeds.
~Client_context()
Destructor; in particular (assuming sync_connect() succeeded) shuts down the Vat_network and the Sess...
Client_context(flow::log::Logger *logger_ptr, kj::AsyncIoContext *kj_io, const session::Client_app &cli_app_ref, const session::Server_app &srv_app_ref, bool enable_hndl_transport=true)
Constructs us without establishing the capnp-RPC session.
bool sync_connect_impl(typename Session_obj::Channels *init_channels_by_cli_req_pre_sized, typename Session_obj::Channels *init_channels_by_srv_req, bool sans_shm_transport)
Impl of sync_connect() and sync_connect_sans_shm_transport().
typename Protocol::socket Peer_socket
Short-hand for boost.asio Unix domain peer stream-socket (usually-connected-or-empty guy).
Provides Flow-IPC's integration with Cap'n Proto's Remote Procedure Call (RPC) layer,...
std::ostream & operator<<(std::ostream &os, const Client_context< Client_session_t > &val)
Prints string representation of the given Client_context to the given ostream.
boost::asio::const_buffer Blob_const
Short-hand for an immutable blob somewhere in memory, stored as exactly a void const * and a size_t.
Log_component
The flow::log::Component payload enumeration containing various log components used by Flow-IPC inter...
flow::Error_code Error_code
Short-hand for flow::Error_code which is very common.
std::string m_name
Brief application name, readable to humans and unique across all other applications' names; used both...
An App that is used as a client in at least one client-server IPC split.
An App that is used as a server in at least one client-server IPC split.
A monolayer-thin wrapper around a native handle, a/k/a descriptor a/k/a FD.
constexpr bool null() const noexcept
Returns true if and only if m_native_handle equals S_NULL_HANDLE.
void close() noexcept
Little utility that returns m_native_handle to the OS.