Flow-IPC 1.0.1
Flow-IPC project: Public API.
Public Types | Public Member Functions | Static Public Attributes | List of all members
ipc::session::Session< Mdt_payload > Class Template Reference

A documentation-only concept defining the local side of an IPC conversation (session) with another entity (typically a separate process), also represented by a Session-implementing object, through which one can easily open IPC channels (ipc::transport::Channel), among other IPC features. More...

#include <session.hpp>

Public Types

using Channel_obj = unspecified
 Each successful open_channel() and on-passive-open handler firing shall yield a concrete transport::Channel instance of this type – in PEER state. More...
 
using Mdt_payload_obj = Mdt_payload
 Short-hand for Mdt_payload template arg.
 
using Mdt_builder = typename transport::struc::schema::Metadata< Mdt_payload_obj >::Builder
 Pointee of Mdt_builder_ptr.
 
using Mdt_builder_ptr = shared_ptr< Mdt_builder >
 Ref-counted handle to a capnp-generated Builder (and the payload it accesses) through which the user shall mutate the open_channel() metadata before invoking open_channel(). More...
 
using Mdt_reader_ptr = shared_ptr< typename transport::struc::schema::Metadata< Mdt_payload_obj >::Reader >
 Ref-counted handle to a capnp-generated Reader (and the payload it accesses) through which the user shall access the metadata the other side prepared via Mdt_builder_ptr. More...
 
template<typename Message_body >
using Structured_channel = transport::struc::Channel< unspecified >
 Convenience alias for the transport::struc::Channel concrete type to use if one desires (1) to upgrade a *this-generated channel to a struc::Channel and (2) to efficiently use the built-in capabilities of *this (notably, if applicable: SHM) for zero-copy performance when sending messages through that channel. More...
 
using Structured_msg_builder_config = unspecified
 Convenience alias equal to Structured_channel<M>::Builder_config (regardless of M). More...
 
using Structured_msg_reader_config = unspecified
 Convenience alias equal to Structured_channel<M>::Reader_config (regardless of M). More...
 

Public Member Functions

 Session ()
 Default ctor: creates Session in NULL state (not operational). More...
 
 Session (Session &&src)
 Move ctor: *this becomes identical to src; while src becomes as-if default-cted. More...
 
 Session (const Session &)=delete
 Copy construction is disallowed.
 
 ~Session ()
 In NULL state, no-op; in PEER state: Ends the session while informing (if possible) the opposing peer Session that the present session has ended. More...
 
Sessionoperator= (Session &&src)
 Move assignment: acts as-if *this dtor executed; then *this becomes identical to src; while src becomes as-if default-cted. More...
 
Sessionoperator= (const Session &)=delete
 Copy assignment is disallowed.
 
Mdt_builder_ptr mdt_builder ()
 Returns a new metadata holder to be subsequently mutated by the user and then passed to open_channel() to initiate active-open attempt. More...
 
bool open_channel (Channel_obj *target_channel, const Mdt_builder_ptr &mdt, Error_code *err_code=0)
 Synchronously active-opens a new channel which, on success, is moved-to *target_channel. More...
 
bool open_channel (Channel_obj *target_channel, Error_code *err_code=0)
 Identical to open_channel(target_channel, mdt_builder(), err_code); in other words attempts to open channel with an uninitialized channel-open metadata payload. More...
 
const Session_tokensession_token () const
 In PEER state: Returns the (non-nil) logged-in session token to be used for any struc::Channels one may wish to upgrade from Channel_obj channels yielded by open_channel() or passive-opens; or nil if in NULL state, or if *this session is hosed from a prior error. More...
 

Static Public Attributes

static constexpr schema::ShmType S_SHM_TYPE = unspecified
 Specifies the SHM-provider for which this Session concept implementation provides support; or NONE if this is a vanilla Session that does not provide SHM-based zero-copy support. More...
 
static constexpr bool S_SHM_ENABLED = unspecified
 Specifies whether this Session concept implementation provides support for zero-copy via SHM plus direct access to SHM arenas and lending/borrowing within them. More...
 

Detailed Description

template<typename Mdt_payload = ::capnp::Void>
class ipc::session::Session< Mdt_payload >

A documentation-only concept defining the local side of an IPC conversation (session) with another entity (typically a separate process), also represented by a Session-implementing object, through which one can easily open IPC channels (ipc::transport::Channel), among other IPC features.

While it is possible to open a Channel without the help of this Session concept, for many users writing large multi-channel applications it would be too laborious to do so. The Session concept is the central one in ipc::session.

See also
ipc::session doc header.
Session_mv which implements the majority of Session concept, namely when it is in PEER state.

Concept contents

The concept defines the following behaviors/requirements.

The concept (intentionally) does not define the following behaviors:

Async-I/O channels versus sync_io ones

There are 3, overall, ways to obtain a transport::Channel via ipc::session.

In all cases these channels shall bear sync_io-pattern peer objects: not async-I/O ones.

Thread safety and handler invocation semantics

As is typical: 2 different Session objects are safe to invoke methods on concurrently; it is not safe to invoke a non-const method on a given *this concurrently to any other method on the same *this.

The handlers – namely the passive-open handler (optional) and the on-error handler (mandatory) – shall be invoked from some unspecified thread that is not any one of the user's calling threads. It is not allowed to invoke *this APIs from within any such handler. For example, you may not open_channel() from the passive-open handler. Informally, it is recommended that any user handler post its actual handling of the event onto their own thread(s), such as via flow::async::Single_thread_task_loop::post().

Passive-open handler semantics

As summarized above, a Session – at entry to PEER state – is optionally capable of accepting the partner Session's active-open (open_channel()) attempt. The user must make the decision whether to be capable or incapable of performing such a passive-open. If they decide the latter, they must not set the on-passive-open handler; else they must set it by the time PEER state begins. (The mechanism of doing so is unspecified by the concept. The current impls, Server_session and Client_session, use different APIs for this.)

If the choice is made to indeed perform passive-opens (accept other side's active-opens), then suppose the handler H is indeed saved inside *this (which is in PEER state). Then the following shall be invoked from some unspecified thread that is not one of the user's public API-invoking threads: H(C, M), where C is a Channel_obj onto which the new PEER-state transport::Channel shall be move()d; while M is the metadata reader (explained below). Therefore the H signature must be compatible with:

void H(Channel_obj&& new_peer_channel, Mdt_reader_ptr&& metadata_reader)
unspecified Channel_obj
Each successful open_channel() and on-passive-open handler firing shall yield a concrete transport::C...
Definition: session.hpp:235
shared_ptr< typename transport::struc::schema::Metadata< Mdt_payload_obj >::Reader > Mdt_reader_ptr
Ref-counted handle to a capnp-generated Reader (and the payload it accesses) through which the user s...
Definition: session.hpp:262

Note that Mdt_reader_ptr is a shared_ptr to a capnp-generated Reader to a capnp-struct parameterized on Mdt_payload_obj. The memory resources taken by the serialization of the metadata (including certain internal zero-copy "baggage") shall be freed once that shared_ptr ref-count group is empty. As for what the heck this even is: see open_channel()'s metadata arg docs. This is the Reader counterpart to the Builder mutated by the opposing open_channel() caller.

Open channel metadata

open_channel() is used to initiate a channel opening on one side, and the on-passive-open handler is used to accept the same on the other side (for a given channel). The result, on each side, is a Channel_obj, which is a Channel template instantiation. Note that it is not a struc::Channel; the user may choose to immediate convert Channel to struc::Channel via std::move(), but whether they want to or not is up to them and their desired use case. Now, there are millions of patterns of what the acceptor might want to accept in a session; there could be one channel for some app-wide updates and one per processor core; and possibly more differentiation than that regarding which channel is for what purpose. Meanwhile the only way to know which channel is being opened, other than a set ordering (certainly inconvenient at best in many cases), is to perhaps exchange some information on the channels themselves. That too is inconvenient: even having discovered that a certain channel is for a certain purpose, distributing them around the app is no fun probably. Furthermore, it's only a Channel: is the user supposed to encode something in a binary blob? Inconvenient. But if one turns it into a struc::Channel, then that is irreversible and may not be what the user wants. The solution we use: the open_channel() call is issued with a separate (to any structured in-message the user would see) chunk of capnp-structured metadata. This is transmitted and supplied to the on-passive-open handler per the preceding doc section. Details:

It is encoded in a capnp-struct Metadata, defined in common.capnp. However that struct is generically parameterized on another capnp-type of the user's choice. That is the Session template arg Mdt_payload_obj. Hence the user shall decide how to encode the channel-differentiating info on a per-Session basis. capnp shall generate the usual Reader and Builder nested classes; the latter to build it to pass into open_channel(); the former to read it, the on-passive-open handler having fired. Call capnp-generated Builder setters and Reader getters accordingly on either side.

This feature is optional to use. You may set Mdt_payload_obj to capnp::Void; omit the metadata argument to open_channel(); and ignore the Reader counterpart in the on-passive-open handler.

Informal suggestion: While Session concept does not specify anything about clients or servers, as of this writing the client-server model is the only one available, hence Client_session and Server_session are the impls used. Server_sessions are produced, in this model, via Session_server. The latter class template requires that Mdt_payload be specified as a template arg to be applied to all Server_session objects it produces. Therefore it must be decided not just for each session but realistically for the entire client/server split (in other words the entire meta-application that was split into two): all the Server_sessions; and therefore to all the Client_sessions as well (since the 2 are peers to each other). So the method of signaling channel type (if needed) via metadata is quite a high-level decision. One obvious idea is to maintain a split-wide capnp schema that specifies the struct to use as Mdt_payload; in that struct define perhaps a capnp-enum type for channel type. Alternatively one can define a top-level anon capnp-union; its which() selector can function as an enumeration (in fact it is one in the generated C++); and any further data (e.g., the processor core index, if there's some channel-per-core setup) inside that particular which()'s struct. Do be careful to (1) maintain backward and forward compatibility as channel types are added over time; and (2) do not abuse the metadata store with large structures. Anything that is not directly used in differentiating between channels should simply be communicated over the channel itself, once it has been routed to the proper area of code; that's what transport::struc::Channel is for in particular.

Error handling semantics

There are 2 sources of errors: background (async) errors and synchronously from invoking active-open: open_channel(). A given error is either session-hosing (fatal) or not. To make handling error simpler these are the semantics:

Even if, internally, open_channel() in some way triggers a session-hosing error, it shall be emitted as-if it were an async error in a way indistinguishable in practice from any other async error.

When a session-hosing error occurs, it is emitted exactly once (through the on-error handler). After such an event, open_channel() and other APIs shall no-op/return sentinel subsequently if invoked.

This is simpler than how transport::struc::Channel emits errors; there the (always synchronous) send() can emit an error synchronously that is session-hosing; in addition to incoming-direction session-hosing errors; but still only one of these can occur and only once. So in that sense open_channel() is not like send(): if it emits an error, *this is still formally in non-session-hosed state.

However, of course, the user is free to treat an open_channel()-emitted error as fatal and destroy *this.

See also
Channel and buddies: the main object of opening a Session is to be able to easily active-open and/or passive-open Channels in PEER state through it.
Client_session, Server_session: as of this writing, any non-SHM-enabled pair of PEER-state Sessions is a Client_session and a Server_session. Server_sessions in a process, designated as the server in an IPC split, are obtained (following the acceptor pattern) via a single Session_server in that process. On the other side, a Client_session is directly constructed (in NULL state) and enters PEER state via Client_session::sync_connect(). (Conceptually this is similar to the relationship between, e.g., Native_socket_stream (server side), Native_socket_stream_acceptor, and Native_socket_stream (client side). However, it so happens that in the case of Sessions, a different class is used depending on which side the Session sits. Since they both implement the same concept, however, code can be written that behaves identically – given any Session in PEER state – regardless of whether it's really a Client_session or Server_session. That said, such code does have to be templated on the particular Session as a template param.)
E.g., shm::classic::Client_session and shm::classic::Server_session which are an example of a SHM-enabled variant of Session impls.
Template Parameters
Mdt_payloadcapnp-generated class for the user's capnp-struct of choice for open_channel() transmitting metadata information to be received on the other side's on-passive-open handler. See discussion above. Use capnp::Void if not planning to use this metadata feature.

Member Typedef Documentation

◆ Channel_obj

template<typename Mdt_payload = ::capnp::Void>
using ipc::session::Session< Mdt_payload >::Channel_obj = unspecified

Each successful open_channel() and on-passive-open handler firing shall yield a concrete transport::Channel instance of this type – in PEER state.

The concept does not specify how the concrete Channel instance type – meaning the template params to Channel – is determined. In practice it is likely to be controlled by unspecified compile-time knobs, likely additional template params, to the particular Session impl. E.g., see Client_session:S_MQS_ENABLED.

In practice a user is likely to declare Channel variables by using this alias (or via auto).

Note
Channel_obj shall be such that its compile-time transport::Channel::S_IS_SYNC_IO_OBJ is true. E.g., if it's got a native-socket-stream, it'll be transport::sync_io::Native_socket_stream – not transport::Native_socket_stream. If you want one with transport::Channel::S_IS_ASYNC_IO_OBJ then use transport::Channel::async_io_obj() (or to just get the type at compile time: transport::Channel::Async_io_obj).

◆ Mdt_builder_ptr

template<typename Mdt_payload = ::capnp::Void>
using ipc::session::Session< Mdt_payload >::Mdt_builder_ptr = shared_ptr<Mdt_builder>

Ref-counted handle to a capnp-generated Builder (and the payload it accesses) through which the user shall mutate the open_channel() metadata before invoking open_channel().

mdt_builder() returns a new one; then user mutates it; then user calls open_channel(). To mutate an x of this type: auto root = x->initPayload();, then mutate via the root sub-builder (standard capnp-generated API for whatever fields capnp-struct Mdt_payload_obj defines).

◆ Mdt_reader_ptr

template<typename Mdt_payload = ::capnp::Void>
using ipc::session::Session< Mdt_payload >::Mdt_reader_ptr = shared_ptr<typename transport::struc::schema::Metadata<Mdt_payload_obj>::Reader>

Ref-counted handle to a capnp-generated Reader (and the payload it accesses) through which the user shall access the metadata the other side prepared via Mdt_builder_ptr.

On-passive-open handler yields a handle of this type. To access via an x of this type: auto root = x->getPayload();, then access via the root sub-reader (standard capnp-generated API for whatever fields capnp-struct Mdt_payload_obj defines).

◆ Structured_channel

template<typename Mdt_payload = ::capnp::Void>
template<typename Message_body >
using ipc::session::Session< Mdt_payload >::Structured_channel = transport::struc::Channel<unspecified>

Convenience alias for the transport::struc::Channel concrete type to use if one desires (1) to upgrade a *this-generated channel to a struc::Channel and (2) to efficiently use the built-in capabilities of *this (notably, if applicable: SHM) for zero-copy performance when sending messages through that channel.

Note
This alias is to an async-I/O struc::Channel. (See util::sync_io doc header for background on the 2 patterns, async-I/O and sync_io.) To obtain the sync_io-core counterpart type use: Session::Structured_channel::Sync_io_obj.

Informal suggestion

Use this (parameterized by Message_body of choice of course) as your transport::struc::Channel concrete type. Then use the appropriate tag-form struc::Channel ctor. Namely as of this writing:

Template Parameters
Message_bodySee transport::struc::Channel. All the other annoying decisions are made for you using this alias; but of course you must still specify the language you shall be speaking over the channel.

◆ Structured_msg_builder_config

template<typename Mdt_payload = ::capnp::Void>
using ipc::session::Session< Mdt_payload >::Structured_msg_builder_config = unspecified

Convenience alias equal to Structured_channel<M>::Builder_config (regardless of M).

See Structured_channel.

Informally: The impl template may choose to provide methods to obtain a Structured_msg_builder_config to use for a Structured_channel::Msg_out that can be constructed without a concrete Structured_channel available. These methods might in some cases even be static (in which case they should take a Logger* logger_ptr 1st arg), so that not even having a Session object in hand is needed.

◆ Structured_msg_reader_config

template<typename Mdt_payload = ::capnp::Void>
using ipc::session::Session< Mdt_payload >::Structured_msg_reader_config = unspecified

Convenience alias equal to Structured_channel<M>::Reader_config (regardless of M).

See Structured_channel. The utility of explicit use of this alias by the user is less than Structured_msg_builder_config, as a transport::struc::Msg_in is not constructed directly but by transport::struc::Channel internals.

Constructor & Destructor Documentation

◆ Session() [1/2]

template<typename Mdt_payload = ::capnp::Void>
ipc::session::Session< Mdt_payload >::Session ( )

Default ctor: creates Session in NULL state (not operational).

Beyond this and the move ctor, other ctors are unspecified by the concept but may well exist. See discussion in concept doc header.

The practical intended use for this ctor is as a target for a subsequent move-to assignment. For example, Session_server::async_accept() would typically be given a default-cted Server_session which, on success, is moved-to to enter PEER state.

◆ Session() [2/2]

template<typename Mdt_payload = ::capnp::Void>
ipc::session::Session< Mdt_payload >::Session ( Session< Mdt_payload > &&  src)

Move ctor: *this becomes identical to src; while src becomes as-if default-cted.

Parameters
srcMoved-from session that enters NULL state.

◆ ~Session()

template<typename Mdt_payload = ::capnp::Void>
ipc::session::Session< Mdt_payload >::~Session ( )

In NULL state, no-op; in PEER state: Ends the session while informing (if possible) the opposing peer Session that the present session has ended.

This may incur a short delay before the dtor exits (in practice likely to flush out any pending sends over an internal session master channel, informally speaking).

Any Channel_obj previously yielded by open_channel() and/or passive-opens shall continue operating, if still operating, and it is up to the user to perform any cleanup/flushing/etc. on these. This is consistent with the idea that open_channel() and passive-open passes ownership of such channels to the user.

There are 2 mutually exclusive expected triggers for why this dtor would be invoked:

  • The present process is exiting gracefully (hence the session must end). User invokes dtor. Dtor informs the other side with maximum expediency; then returns.
  • The opposing process is exiting gracefully or otherwise; the opposing Session thus informed ours gracefully or otherwise; therefore *this informs user of this via emitted error (see concept doc header regarding session-hosing error emission semantics); therefore *this is no longer usable; hence user invokes *this dtor. It tries to inform the other side, but there's nothing to inform; it quickly returns.

Member Function Documentation

◆ mdt_builder()

template<typename Mdt_payload = ::capnp::Void>
Mdt_builder_ptr ipc::session::Session< Mdt_payload >::mdt_builder ( )

Returns a new metadata holder to be subsequently mutated by the user and then passed to open_channel() to initiate active-open attempt.

The payload (see Mdt_payload_obj) is left uninitialized: the user would likely x->initPayload() and then mutate stuff inside that (where x was returned).

If *this is not in PEER state, returns null.

This call is not needed, if one plans to use open_channel() overload that takes no metadata.

Returns
See above.

◆ open_channel() [1/2]

template<typename Mdt_payload = ::capnp::Void>
bool ipc::session::Session< Mdt_payload >::open_channel ( Channel_obj target_channel,
const Mdt_builder_ptr mdt,
Error_code err_code = 0 
)

Synchronously active-opens a new channel which, on success, is moved-to *target_channel.

No-op and return false if *this is not in PEER state, the session is hosed by a previous error, or mdt is null. Else returns true.

mdt shall be from an mdt_builder() call, and mdt must not have been passed to open_channel() previously; otherwise behavior undefined. See also the open_channel() overload that takes no mdt.

Note
This is a compile-time fact, but we point it out just the same: *target_channel is of a sync_io-core-bearing type. However, on successful open_channel() you can trivially obtain an async-I/O version via target_channel->async_io_obj().

Is it blocking or non-blocking?

That's a matter of perspective; but formally it must be either non-blocking or, if blocking, then limited to a timeout no greater than seconds (not minutes). That said, informally, the implementation shall strive to make this quite fast, enough so to be considered non-blocking, as long as both Session objects are in PEER state.

Error emission

If false is returned, even if open_channel() internally triggered a session-hosing condition, it shall be fired through the on-error handler and not emitted by open_channel().

If true is returned an error may be detected and is then emitted via standard Flow semantics (if err_code then set *err_code to error; otherwise throw). Such an error is not session-hosing.

In particular if passive-opens are disabled on the opposing side it must emit error::Code::S_SESSION_OPEN_CHANNEL_REMOTE_PEER_REJECTED_PASSIVE_OPEN. Informally: an impl is likely to be capable of emitting the non-fatal error::Code::S_SESSION_OPEN_CHANNEL_ACTIVE_TIMEOUT, though the user is reasonably likely to want to treat it as fatal in practice and end the session.

Parameters
target_channelOn success *target_channel is moved-to from a (new) Channel_obj in PEER state.
mdtSee mdt_builder(). If mdt is null, open_channel() returns false.
err_codeSee flow::Error_code docs for error reporting semantics. Error_code generated: error::Code::S_SESSION_OPEN_CHANNEL_REMOTE_PEER_REJECTED_PASSIVE_OPEN, other possible unspecified errors according to impl's discretion. See above discussion.
Returns
See above.

◆ open_channel() [2/2]

template<typename Mdt_payload = ::capnp::Void>
bool ipc::session::Session< Mdt_payload >::open_channel ( Channel_obj target_channel,
Error_code err_code = 0 
)

Identical to open_channel(target_channel, mdt_builder(), err_code); in other words attempts to open channel with an uninitialized channel-open metadata payload.

Informally: this overload can be used if differentiation among channels, for the passive-open side's benefit, is not necessary. In that case Mdt_payload_obj is typically set to capnp::Void.

Note
This is a compile-time fact, but we point it out just the same: *target_channel is of a sync_io-core-bearing type. However, on successful open_channel() you can trivially obtain an async-I/O version via target_channel->async_io_obj().
Parameters
target_channelSee other open_channel().
err_codeSee other open_channel().
Returns
See other open_channel(). However it cannot return false due to an empty mdt, as mdt is internally generated (and left uninitialized).

◆ operator=()

template<typename Mdt_payload = ::capnp::Void>
Session & ipc::session::Session< Mdt_payload >::operator= ( Session< Mdt_payload > &&  src)

Move assignment: acts as-if *this dtor executed; then *this becomes identical to src; while src becomes as-if default-cted.

No-op if &src == this.

Parameters
srcMoved-from session that enters NULL state (unless &src == this).
Returns
*this.

◆ session_token()

template<typename Mdt_payload = ::capnp::Void>
const Session_token & ipc::session::Session< Mdt_payload >::session_token ( ) const

In PEER state: Returns the (non-nil) logged-in session token to be used for any struc::Channels one may wish to upgrade from Channel_obj channels yielded by open_channel() or passive-opens; or nil if in NULL state, or if *this session is hosed from a prior error.

Informal context: Firstly see transport::struc::Channel::session_token() and class template doc header. However note that open_channel() (active-open) and on-passive-open handler yields an unstructured, unused Channel_obj: it is entirely optional that the user then upgrade it to a struc::Channel. If one does so, however, then a non-nil session-token shall be required and, for safety, must equal what this session_token() returns.

Returns
See above. The reference returned shall always be one of 2: a certain unspecified internal item, or to transport::struc::NULL_SESSION_TOKEN.

Member Data Documentation

◆ S_SHM_ENABLED

template<typename Mdt_payload = ::capnp::Void>
constexpr bool ipc::session::Session< Mdt_payload >::S_SHM_ENABLED = unspecified
staticconstexpr

Specifies whether this Session concept implementation provides support for zero-copy via SHM plus direct access to SHM arenas and lending/borrowing within them.

May be useful for generic programming; perhaps in std::conditional or if constexpr() uses.

See also
S_SHM_TYPE

◆ S_SHM_TYPE

template<typename Mdt_payload = ::capnp::Void>
constexpr schema::ShmType ipc::session::Session< Mdt_payload >::S_SHM_TYPE = unspecified
staticconstexpr

Specifies the SHM-provider for which this Session concept implementation provides support; or NONE if this is a vanilla Session that does not provide SHM-based zero-copy support.

May be useful for generic programming; perhaps in std::conditional or if constexpr() uses. Informally we recommend against frequent use of this except logging/reporting/alerting and debug/test scenarios.

See also
S_SHM_ENABLED.

The documentation for this class was generated from the following file: