Flow-IPC 1.0.0
Flow-IPC project: Public API.
Public Types | Public Member Functions | Static Public Attributes | Related Functions | List of all members
ipc::transport::Channel< Blob_sender, Blob_receiver, Native_handle_sender, Native_handle_receiver > Class Template Reference

Peer to a bundle of 1-2 full-duplex pipe(s), one for transmitting unstructured binary blobs; the other for transmitting native handle+blob combos; hence a Blob_sender + Blob_receiver, a Native_handle_sender + Native_handle_receiver, or both. More...

#include <channel.hpp>

Inherits flow::log::Log_context.

Public Types

using Blob_sender_obj = Blob_sender
 Alias for Blob_sender template parameter.
 
using Blob_receiver_obj = Blob_receiver
 Alias for Blob_receiver template parameter.
 
using Native_handle_sender_obj = Native_handle_sender
 Alias for Native_handle_sender template parameter.
 
using Native_handle_receiver_obj = Native_handle_receiver
 Alias for Native_handle_receiver template parameter.
 
using Async_io_obj = Channel< typename Blob_sender_obj::Async_io_obj, typename Blob_receiver_obj::Async_io_obj, typename Native_handle_sender_obj::Async_io_obj, typename Native_handle_receiver_obj::Async_io_obj >
 Assuming S_IS_SYNC_IO_OBJ yields async-I/O counterpart; else yields Channel<Null_peer, Null_peer, Null_peer, Null_peer>.
 
using Sync_io_obj = Channel< typename Blob_sender_obj::Sync_io_obj, typename Blob_receiver_obj::Sync_io_obj, typename Native_handle_sender_obj::Sync_io_obj, typename Native_handle_receiver_obj::Sync_io_obj >
 Assuming S_IS_ASYNC_IO_OBJ yields sync_io counterpart; else yields Channel<Null_peer, Null_peer, Null_peer, Null_peer>.
 

Public Member Functions

 Channel ()
 Default ctor (Channel is in ??? state; intended to be move-assigned). More...
 
 Channel (flow::log::Logger *logger_ptr, util::String_view nickname_str)
 Constructs Channel in ??? state with the intention to continue initialization via init_blob_pipe() and/or init_native_handle_pipe() call(s). More...
 
 Channel (Channel &&src)
 Move-constructs from src; src becomes as-if default-cted (therefore in ??? state). More...
 
 Channel (const Channel &)=delete
 Copy construction is disallowed.
 
 ~Channel ()
 Destructor: synchronously invokes the destructors for each peer object moved-into *this via init_blob_pipe() and/or init_native_handle_pipe(). More...
 
bool initialized (bool suppress_log=false) const
 Returns true if and only if the required init_blob_pipe() and init_native_handle_pipe() calls have been made, loading exactly the expected peer objects as the template params Blob_sender_obj, Blob_receiver_obj, Native_handle_sender_obj, Native_handle_receiver_obj specify. More...
 
Channeloperator= (Channel &&src)
 Move-assigns from src; *this acts as if destructed; src becomes as-if default-cted (therefore in ??? state). More...
 
Channeloperator= (const Channel &)=delete
 Copy assignment is disallowed.
 
Async_io_obj async_io_obj ()
 Converts a sync_io-peer-bearing *this to a returned async-I/O-peer-bearing new Channel, while *this becomes as-if default-cted. More...
 
const Blob_sender_objblob_snd () const
 Pointer to the immutable owned Blob_sender_obj; null if yet initialized. More...
 
const Blob_receiver_objblob_rcv () const
 Pointer to the immutable owned Blob_receiver_obj; null if not yet initialized. More...
 
const Native_handle_sender_objhndl_snd () const
 Pointer to the immutable owned Native_handle_sender_obj; null if not yet initialized. More...
 
const Native_handle_receiver_objhndl_rcv () const
 Pointer to the immutable owned Native_handle_receiver_obj; null if not yet initialized. More...
 
Blob_sender_objblob_snd ()
 Pointer to the mutable owned Blob_sender_obj; null if yet initialized. More...
 
Blob_receiver_objblob_rcv ()
 Pointer to the mutable owned Blob_receiver_obj; null if not yet initialized. More...
 
Native_handle_sender_objhndl_snd ()
 Pointer to the mutable owned Native_handle_sender_obj; null if not yet initialized. More...
 
Native_handle_receiver_objhndl_rcv ()
 Pointer to the mutable owned Native_handle_receiver_obj; null if not yet initialized. More...
 
bool init_blob_pipe (Blob_sender_obj &&snd_and_rcv)
 Completes initialization of the blobs pipe by taking ownership (via move semantics) of an object that is simultaneously the Blob_sender_obj and Blob_receiver_obj for our end of the blobs pipe. More...
 
bool init_blob_pipe (Blob_sender_obj &&snd, Blob_receiver_obj &&rcv)
 Completes initialization of the blobs pipe by taking ownership (via move semantics) of separate Blob_sender_obj and Blob_receiver_obj objects for our end of the blobs pipe. More...
 
bool init_native_handle_pipe (Native_handle_sender_obj &&snd_and_rcv)
 Analogous to 1-arg init_blob_pipe() but as applied to the handles pipe. More...
 
bool init_native_handle_pipe (Native_handle_sender_obj &&snd, Native_handle_receiver_obj &&rcv)
 Analogous to 2-arg init_blob_pipe() but as applied to the handles pipe. More...
 
size_t send_blob_max_size () const
 Yields blob_snd()-> same method. More...
 
size_t send_meta_blob_max_size () const
 Yields hndl_snd()-> same method. More...
 
size_t receive_blob_max_size () const
 Yields blob_rcv()-> same method. More...
 
size_t receive_meta_blob_max_size () const
 Yields hndl_rcv()-> same method. More...
 
bool send_blob (const util::Blob_const &blob, Error_code *err_code=0)
 Yields blob_snd()-> same method. More...
 
bool send_native_handle (Native_handle hndl_or_null, const util::Blob_const &meta_blob, Error_code *err_code=0)
 Yields hndl_snd()-> same method. More...
 
template<typename Task_err >
bool async_end_sending (Task_err &&on_done_func)
 Performs hndl_snd()-> and/or blob_snd()-> same method, synthesizing completion handlers into one if applicable. More...
 
template<typename Task_err >
bool async_end_sending (Error_code *sync_err_code, Task_err &&on_done_func)
 Performs hndl_snd()-> and/or blob_snd()-> same method, synthesizing completions into one if applicable. More...
 
bool end_sending ()
 Performs hndl_snd()-> and/or blob_snd()-> same method, returning true if all (1-2) invoked methods returned true; false conversely. More...
 
bool auto_ping (util::Fine_duration period=boost::chrono::seconds(2))
 Performs hndl_snd()-> and/or blob_snd()-> same method, returning true if all (1-2) invoked methods returned true; false conversely. More...
 
template<typename... Args>
bool async_receive_blob (Args &&... args)
 Yields blob_rcv()-> same method. More...
 
template<typename... Args>
bool async_receive_native_handle (Args &&... args)
 Yields hndl_rcv()-> same method. More...
 
bool idle_timer_run (util::Fine_duration timeout=boost::chrono::seconds(5))
 Performs hndl_rcv()-> and/or blob_rcv()-> same method, returning true if all (1-2) invoked methods returned true; false conversely. More...
 
template<typename Create_ev_wait_hndl_func >
bool replace_event_wait_handles (const Create_ev_wait_hndl_func &create_ev_wait_hndl_func)
 Executes same method on all unique stored peer objects; returns true if and only if they all did. More...
 
template<typename Event_wait_func_t >
bool start_send_blob_ops (Event_wait_func_t &&ev_wait_func)
 Yields blob_snd()-> same method. More...
 
template<typename Event_wait_func_t >
bool start_send_native_handle_ops (Event_wait_func_t &&ev_wait_func)
 Yields hndl_snd()-> same method. More...
 
template<typename Event_wait_func_t >
bool start_receive_blob_ops (Event_wait_func_t &&ev_wait_func)
 Yields blob_rcv()-> same method. More...
 
template<typename Event_wait_func_t >
bool start_receive_native_handle_ops (Event_wait_func_t &&ev_wait_func)
 Yields hndl_rcv()-> same method. More...
 
const std::string & nickname () const
 Returns nickname, a brief string suitable for logging. More...
 

Static Public Attributes

static constexpr bool S_HAS_2_PIPES
 Useful for generic programming: true if and only if types imply both blobs and handles pipes are enabled.
 
static constexpr bool S_HAS_BLOB_PIPE_ONLY
 Useful for generic programming: true if and only if types imply only the blobs pipe is enabled.
 
static constexpr bool S_HAS_NATIVE_HANDLE_PIPE_ONLY = (!S_HAS_2_PIPES) && (!S_HAS_BLOB_PIPE_ONLY)
 Useful for generic programming: true if and only if types imply only the handles pipe is enabled.
 
static constexpr bool S_HAS_BLOB_PIPE = S_HAS_2_PIPES || S_HAS_BLOB_PIPE_ONLY
 Useful for generic programming: true if and only if types imply at least the blobs pipe is enabled.
 
static constexpr bool S_HAS_NATIVE_HANDLE_PIPE = S_HAS_2_PIPES || S_HAS_NATIVE_HANDLE_PIPE_ONLY
 Useful for generic programming: true if and only if types imply at least the handles pipe is enabled.
 
static constexpr bool S_IS_SYNC_IO_OBJ
 Useful for generic programming: true <=> each non-Null_peer peer type (Blob_sender_obj, Blob_receiver_obj, Native_handle_sender_obj, Native_handle_receiver_obj) implements the sync_io pattern (by convention living in namespace ipc::transport::sync_io).
 
static constexpr bool S_IS_ASYNC_IO_OBJ = !S_IS_SYNC_IO_OBJ
 It equals the reverse of S_IS_SYNC_IO_OBJ. More...
 

Related Functions

(Note that these are not member functions.)

template<typename Blob_sender , typename Blob_receiver , typename Native_handle_sender , typename Native_handle_receiver >
std::ostream & operator<< (std::ostream &os, const Channel< Blob_sender, Blob_receiver, Native_handle_sender, Native_handle_receiver > &val)
 Prints string representation of the given Channel to the given ostream. More...
 

Detailed Description

template<typename Blob_sender, typename Blob_receiver, typename Native_handle_sender, typename Native_handle_receiver>
class ipc::transport::Channel< Blob_sender, Blob_receiver, Native_handle_sender, Native_handle_receiver >

Peer to a bundle of 1-2 full-duplex pipe(s), one for transmitting unstructured binary blobs; the other for transmitting native handle+blob combos; hence a Blob_sender + Blob_receiver, a Native_handle_sender + Native_handle_receiver, or both.

Generally a session::Session shall be in charge of opening such Channels between processes, while the user will likely wrap each Channel in a struc::Channel in order to exchange structured (schema-based) messages, at times along with native sockets, through those Channels. (However this is by no means required. One can use the Channel – whether bundling async-I/O peer objects or their sync_io:: counterparts – directly (in unstructured form) as well.)

Main use: bundler of peer sender/receiver objects

This bundling of the local peer objects of 1-2 pipes is the Channel template's core functionality; it is therefore (data-wise – but code-wise as well) an extremely thin wrapper around the stored 2-4 peer objects. At its core it provides:

Note
A key point is that, since we're really just storing 2-4 pointers (+ a nickname and a Logger), there is important freedom as to the pointees' types. The Blob_sender can be either Null_peer (i.e., unused), a transport::Blob_sender (async-I/O pattern), or a transport::sync_io::Blob_sender (sync_io pattern; see util::sync_io doc header for background). (And analogously for each of Blob_receiver, Native_handle_sender, Native_handle_receiver.) So in other words a Channel can bundle async-I/O peer objects or sync_io peer objects. You may use Channel::S_IS_SYNC_IO_OBJ or Channel::S_IS_ASYNC_IO_OBJ to determine which is the case at compile-time for a given Channel type.

Accordingly it provides a very useful method: async_io_obj(). If you have a sync_io-pattern-peer-storing Channel x (i.e., "decltype(x)::S_IS_SYNC_IO_OBJ == true"), simply call x.async_io_obj() to create/return an async-I/O version of it. For example, is x was Channel<transport::sync_io::Native_socket_stream, Null_peer, Null_peer, Null_peer> containing a single sync_io::Native_socket_stream core (acting as a blob-sender and -receiver), then x.async_io_obj() shall return a new channel object with the same structure but with a new async-I/O-pattern (i.e., auto-parallelizing, async-acting) transport::Native_socket_stream which was constructed using the sync_io-core-adopting ctor form (per transport::Blob_sender and transport::Blob_receiver concept).

Generally speaking, APIs such as ipc::session (e.g., session::Session_server::async_accept()) and ipc::transport (e.g., transport::Native_socket_stream::async_accept()) will create and subsume objects in their sync_io-pattern form, sometimes called cores. These are lighter-weight (compared to async-I/O ones) and don't auto-start background threads for example. Yet whenever you want the async-I/O goodies (and you do want them) simply .async_channel() to get yourself that guy from the sync_io core.

Note
Notably struc::Channel (and struc::sync_io::Channel, should you wish to use one directly) subsumes a Channel::S_IS_SYNC_IO_OBJ == true unstructured Channel in every constructor form offered.

Secondary use: itself a peer object: forwarding to stored peers

As a nicety, a Channel itself implements the concepts implemented by each of its stored objects, forwarding API calls to it. For example, if it bundles a Blob_sender and a Native_handle_sender, then it will itself have send_blob() and send_native_handle(), forwarding to the stored Blob_sender and Native_handle_sender respectively.

In addition the following transport::sync_io:: methods are available as syntactic sugar if and only if S_IS_SYNC_IO_OBJ.

How to use: type, initialization

A Channel stores a Blob_sender and Blob_receiver; or a Native_handle_sender and Native_handle_receiver; or both.

The latter is termed the handles pipe; the former the blobs pipe. A Channel can be 1 of 2 Channel peers to a pipe bundle containing either the handles pipe, the blobs pipe, or both. This setting is specified during initialization and cannot be changed after that for a given *this. Initialization consists of the following simple steps:

After this, initialization is finished. The Channel is in PEER state. One can use these approaches:

Informally: one approach to retain sanity might be – for a given *this – to use one or the other approach (for a given object), not both.

Aliases to Channel

To make it much easier to intantiate and initialize a concrete Channel, there are several aliases and glorified aliases (data-free sub-classes). See transport_fwd.hpp for the aliases and the present channel.hpp for the glorified aliases. For example:

Generally such convenience types do not require any init_blob_pipe() or init_native_handle_pipe() calls; one supplies what's necessary at construction time, and the ctor makes those needed calls for the user.

This really applies only when creating Channels not via ipc::session – in its case the exact Channel type is determined internally to ipc::session: you can not worry about typing or construction and just use the guy. However someone somewhere does need to actually type and create a Channel.

Please realize that it is safe and appropriate to static_cast<> between pointers/references of any Channel type to any other Channel type: all that has to be true between a given pair of types C1 and C2 is: is_same_v<C1::Blob_sender_obj, C2::Blob_sender_obj> == true, repeated for Blob_receiver_obj, Native_handle_sender_obj, Native_handle_receiver_obj.

??? versus PEER states; pipe interaction

A Channel contains minimal logic. It's a bundling of pipe peers; and secondarily of concept implementations.

In that secondary role, befitting a Blob_sender, Blob_receiver, and/or Native_handle_sender, Native_handle_receiver: A Channel *this is in one of 2 states:

As per those concepts: The only way to exit PEER state is to move-from *this which makes it ??? (as-if default-cted) again.

As of this writing, in ??? state methods other than init*() and the basic accessors shall have undefined behavior. In PEER state however they shall all strive to do work (per concepts). There's technically also the no-man's-land wherein one has called some but not all intended init_*_pipe() calls. Behavior is similarly undefined in that no-man's-land.

To restate, indeed a Channel contains minimal logic and mainly bundles 1-2 pipes, supplying at least 1 bidirectional pipe and optionally a way to transmit native handles along one if desired. Mostly its API keeps the pipes (if there are indeed 2) separate: send_blob() affects one, send_native_socket() affects the other; the calls do not interact; similarly for receiving. What minimal interaction does occur does so in those APIs that have identical signatures in each concept pair Native_handle_sender/Blob_sender, Native_handle_receiveir/Blob_receiver. These interactions are documented in the respective methods' doc headers. For example async_end_sending(F) shall invoke F(), once each pipe's individual *end_sending() has completed; and errors (if any) will be reported in a particular way. Additionally note the following:

Note on error handling/pipe hosing

Again *this is just bundling some senders/receivers. To begin with, a sender and receiver for one of the pipes (if there are two; or simply the pipe otherwise) might be the same object, such as with Native_socket_stream; or separate ones such as Blob_stream_mq_sender and Blob_stream_mq_receiver. Furthermore there might be 2 pipes, one for blobs, the other for sockets. Now: Consider "pipe hosing." A pipe is hosed (permanently) when an error is emitted to the async transmission methods async_receive_blob(), async_receive_native_handle(), or *end_sending(); or (in most cases) the synchronous ones send_blob(), send_native_handle(). The question is... which pipe is hosed? The answer: it depends on what is being bundled here and the type of error. If Native_socket_stream::send_blob() fails (because Blob_sender_obj is Native_socket_stream), with system error connection-reset, then both the in-pipe (within the blobs pipe) and out-pipe (ditto) are hosed, because when a Unix domain socket goes down with connection-reset, that means both directions are hosed. If async_receive_blob() fails with graceful-close error::Code::S_RECEIVES_FINISHED_CANNOT_RECEIVE, then only the in-pipe is hosed, while the out-pipe is fine. On the other hand with Blob_stream_mq_sender::send_blob() (because Blob_sender_obj is Blob_stream_mq_sender), no matter how it fails, it does not affect the out-pipe which is presumably run through an entirely separate Blob_stream_mq_receiver, operating necessarily over a different underlying MQ. And in either case, if there is also a 2nd bidirectional pipe also bundled, then errors over the first pipe mean nothing to the second.

So, it depends, and in non-trivial ways very much dependent on the concrete types given as template params.

Therefore we make an informal recommendation to the user of any Channel but especially so if programming generically (meaning without awareness of what type that particular Channel is):

If a pipe-hosing error is detected through any Channel API, prefer to subsequently treat the other pipes as hosed (even if it/they may still be capable of operating). Stop using the Channel; consider invoking async_end_sending(F) (or sync_io-pattern form async_end_sending(E, F)); and once F() is executed (or sync_io equivalent; or *end_sending() returns false) then perhaps destroy *this. Do not try to keep using any remaining 1- or 2-directional pipe(s).

Your code will be simpler – probably without losing much functionality.

A potential exception to this rule of thumb could be error::Code::S_RECEIVES_FINISHED_CANNOT_RECEIVE, as it indicates the graceful closing of an in-pipe, while the out-pipe could be fine... but not really; because what about the other bidirectional pipe (if applicable)? That one is fine all-around, both directions; so now what?

Therefore in our opinion it is really best, typically, to look for any 1-direction-pipe-hosing error, and once detected:

  1. End regular use of *this Channel.
  2. (Optional depending on sensitivity of your task/data) Invoke async_end_sending(). If it returns false, or once the completion handler executes (or the sync_io form synchronously succeeds):
  3. Destroy *this Channel (to avoid unnecessary resource use including background threads in the async-I/O-pattern case).

Thread safety

This flows out of understanding the above explanation wherein it is shown that: Strictly after initialization and excluding the acting-on-the-bundle methods end_sending(), async_end_sending(), auto_ping(), idle_timer_run(), the rest of the methods – most notably async_receive_*() and send_*() – operate on each pipe (if there are 2, not 1) independently. Therefore async_receive_blob() work occurs on entirely separate data from async_receive_native_handle() work; and similarly send_blob() versus send_native_handle(). Therefore running such operations concurrently is safe.

Certainly, post-initialization, accessors {blob|hndl}_{snd|rcv}() are safe to invoke concurrently.

Template Parameters
Blob_senderImplements that concept in transport:: or transport::sync_io:: if blobs pipe enabled; else Null_peer. If not Null_peer, and another 1 of remaining 3 parameters is not Null_peer, then they must both be in transport:: or both in transport::sync_io::.
Blob_receiverImplements that concept in transport:: or transport::sync_io:: if blobs pipe enabled; else Null_peer. If not Null_peer, and another 1 of remaining 3 parameters is not Null_peer, then they must both be in transport:: or both in transport::sync_io::.
Native_handle_senderImplements that concept in transport:: or transport::sync_io:: if handles pipe enabled; else Null_peer. If not Null_peer, and another 1 of remaining 3 parameters is not Null_peer, then they must both be in transport:: or both in transport::sync_io::.
Native_handle_receiverImplements that concept in transport:: or transport::sync_io:: if handles pipe enabled; else Null_peer. If not Null_peer, and another 1 of remaining 3 parameters is not Null_peer, then they must both be in transport:: or both in transport::sync_io::.
See also
Blob_sender: possible implemented concept.
sync_io::Blob_sender: alternative possible implemented concept (not both).
Blob_receiver: possible implemented concept.
sync_io::Blob_receiver: alternative possible implemented concept (not both).
Native_handle_sender: possible implemented concept.
sync_io::Native_handle_sender: alternative possible implemented concept (not both).
Native_handle_receiver: possible implemented concept.
sync_io::Native_handle_receiver: alternative possible implemented concept (not both).

Constructor & Destructor Documentation

◆ Channel() [1/3]

Default ctor (Channel is in ??? state; intended to be move-assigned).

This ctor is informally intended for the following uses:

  • A moved-from Channel (i.e., the src arg move-ctor and move-assignment operator) becomes as-if defaulted-constructed.
  • A target Channel for an API that generates PEER-state Channel objects shall typically be default-cted by user before being passed to that API.

Therefore it would be unusual (though allowed) to make direct calls such as init_blob_pipe() on a default-cted Channel without first moving a non-default-cted object into it.

◆ Channel() [2/3]

template<typename Blob_sender , typename Blob_receiver , typename Native_handle_sender , typename Native_handle_receiver >
ipc::transport::Channel< Blob_sender, Blob_receiver, Native_handle_sender, Native_handle_receiver >::Channel ( flow::log::Logger *  logger_ptr,
util::String_view  nickname_str 
)

Constructs Channel in ??? state with the intention to continue initialization via init_blob_pipe() and/or init_native_handle_pipe() call(s).

This ctor is informally intended for the following use:

  • You create a Channel that is logger-appointed and nicely-nicknamed; then you call init_blob_pipe() and/or init_native_handle_pipe() to move actual transmitting peer objects into *this, moving *this into PEER state. It will retain the logger and nickname throughout.

It may be more convenient to use an alias or data-less sub-class which typically comes with specialized ctor forms that remove or reduce the need for laborious init_*_pipe() calls.

Parameters
logger_ptrLogger to use for subsequently logging.
nickname_strHuman-readable nickname of the new object, as of this writing for use in operator<<(ostream) and logging only.

◆ Channel() [3/3]

Move-constructs from src; src becomes as-if default-cted (therefore in ??? state).

Parameters
srcSee above.

◆ ~Channel()

Destructor: synchronously invokes the destructors for each peer object moved-into *this via init_blob_pipe() and/or init_native_handle_pipe().

All the notes for the N concepts' destructors apply. In PEER state, for async-I/O peer objects, any pending one-off completion handlers will be invoked with error::Code::S_OBJECT_SHUTDOWN_ABORTED_COMPLETION_HANDLER – and so on.

Member Function Documentation

◆ async_end_sending() [1/2]

template<typename Blob_sender , typename Blob_receiver , typename Native_handle_sender , typename Native_handle_receiver >
template<typename Task_err >
bool ipc::transport::Channel< Blob_sender, Blob_receiver, Native_handle_sender, Native_handle_receiver >::async_end_sending ( Error_code sync_err_code,
Task_err &&  on_done_func 
)

Performs hndl_snd()-> and/or blob_snd()-> same method, synthesizing completions into one if applicable.

Invoke only after initialized() is true; else behavior is undefined. That aside:

  • If not S_HAS_2_PIPES, then simply forwards to the appropriate one method of the enabled pipe.
  • If S_HAS_2_PIPES:
    • on_done_func() is invoked (exactly once) if and only if both forwarded methods have completed.
      • If 1-2 completions do not occur, it is not invoked.
    • If that occurs, it is invoked in-place of the 2nd completion handler (chronologically in order of completion).
    • The Error_code passed to on_done_func() is:
      • falsy (success) if and only both completions were successful;
      • if one failed but not the other, the truthy Error_code from the failed completion;
      • if both failed, the truthy Error_code from the first failed completion.

"Completion" in this case means the first (and only) one to occur for each pipe:

Behavior is undefined (assertion may trip) if there are 2 pipes enabled; and one's .async_end_sending() returns false (dupe call), while the other true. This will not occur, if you cleanly use the Channel API directly on *this only, as opposed to shenanigans via non-const blob_snd(), blob_rcv(), etc.

Compilable only if S_IS_SYNC_IO_OBJ.

Template Parameters
Task_errSee concept API.
Parameters
sync_err_codeSee concept API. Do realize error::Code::S_SYNC_IO_WOULD_BLOCK is still an error, so if this pointer is null, then would-block will make this throw.
on_done_funcSee above.
Returns
If one forwarded invocation: returns what it returned. If two forwarded invocations: returns true if both returned true; false if both returned false; behavior undefined if they returned conflicting values.

◆ async_end_sending() [2/2]

template<typename Blob_sender , typename Blob_receiver , typename Native_handle_sender , typename Native_handle_receiver >
template<typename Task_err >
bool ipc::transport::Channel< Blob_sender, Blob_receiver, Native_handle_sender, Native_handle_receiver >::async_end_sending ( Task_err &&  on_done_func)

Performs hndl_snd()-> and/or blob_snd()-> same method, synthesizing completion handlers into one if applicable.

Invoke only after initialized() is true; else behavior is undefined. That aside:

  • If not S_HAS_2_PIPES, then simply forwards to the appropriate one method of the enabled pipe.
  • If S_HAS_2_PIPES:
    • on_done_func() is invoked (exactly once) if and only if both forwarded methods have completed.
      • If 1-2 completions do not occur, it is not invoked.
    • If that occurs, it is invoked in-place of the 2nd completion handler (chronologically in order of completion).
    • The Error_code passed to on_done_func() is:
      • falsy (success) if and only both completions were successful;
      • if one failed but not the other, the truthy Error_code from the failed completion;
      • if both failed, the truthy Error_code from the first failed completion.

Behavior is undefined (assertion may trip) if there are 2 pipes enabled; and one's .async_end_sending() returns false (dupe call), while the other true. This will not occur, if you cleanly use the Channel API directly on *this only, as opposed to shenanigans via non-const blob_snd(), blob_rcv(), etc.

Compilable only if S_IS_ASYNC_IO_OBJ.

Template Parameters
Task_errSee concept API.
Parameters
on_done_funcSee above.
Returns
If one forwarded invocation: returns what it returned. If two forwarded invocations: returns true if both returned true; false if both returned false; behavior undefined if they returned conflicting values.

◆ async_io_obj()

Converts a sync_io-peer-bearing *this to a returned async-I/O-peer-bearing new Channel, while *this becomes as-if default-cted.

Compilable only if S_IS_SYNC_IO_OBJ, and usable only after initialized(), returns Channel with S_IS_ASYNC_IO_OBJ, each peer object of which is created via its respective sync_io-core-adopting ctors. (For example: transport::Blob_sender(transport::sync_io::Blob_sender&&).)

If not initialized() behavior is undefined (assertion may trip).

Useful, in particular, to make an async-I/O Channel from a Channel obtained from a session::Session.

Returns
See above.

◆ async_receive_blob()

template<typename Blob_sender , typename Blob_receiver , typename Native_handle_sender , typename Native_handle_receiver >
template<typename... Args>
bool ipc::transport::Channel< Blob_sender, Blob_receiver, Native_handle_sender, Native_handle_receiver >::async_receive_blob ( Args &&...  args)

Yields blob_rcv()-> same method.

Blob_receiver versus sync_io::Blob_receiver signatures differ slightly; therefore this uses param-pack perfect forwarding.

Template Parameters
ArgsSee above.
Parameters
argsSee concept API.
Returns
See concept API.

◆ async_receive_native_handle()

template<typename Blob_sender , typename Blob_receiver , typename Native_handle_sender , typename Native_handle_receiver >
template<typename... Args>
bool ipc::transport::Channel< Blob_sender, Blob_receiver, Native_handle_sender, Native_handle_receiver >::async_receive_native_handle ( Args &&...  args)

Yields hndl_rcv()-> same method.

Native_handle_receiver versus sync_io::Native_handle_receiver signatures differ slightly; therefore this uses param-pack perfect forwarding.

Template Parameters
ArgsSee above.
Parameters
argsSee concept API.
Returns
See concept API.

◆ auto_ping()

template<typename Blob_sender , typename Blob_receiver , typename Native_handle_sender , typename Native_handle_receiver >
bool ipc::transport::Channel< Blob_sender, Blob_receiver, Native_handle_sender, Native_handle_receiver >::auto_ping ( util::Fine_duration  period = boost::chrono::seconds(2))

Performs hndl_snd()-> and/or blob_snd()-> same method, returning true if all (1-2) invoked methods returned true; false conversely.

As with async_end_sending() (either overload) behavior is undefined (assertion may trip), if one returned true and the other false.

Parameters
periodSee concept API.
Returns
See above.

◆ blob_rcv() [1/2]

Pointer to the mutable owned Blob_receiver_obj; null if not yet initialized.

Compilable only if Blob_receiver_obj is not Null_peer (S_HAS_BLOB_PIPE).

Returns
See above.

◆ blob_rcv() [2/2]

Pointer to the immutable owned Blob_receiver_obj; null if not yet initialized.

Compilable only if Blob_receiver_obj is not Null_peer (S_HAS_BLOB_PIPE).

Returns
See above.

◆ blob_snd() [1/2]

Pointer to the mutable owned Blob_sender_obj; null if yet initialized.

Compilable only if Blob_sender_obj is not Null_peer (S_HAS_BLOB_PIPE).

Returns
See above.

◆ blob_snd() [2/2]

Pointer to the immutable owned Blob_sender_obj; null if yet initialized.

Compilable only if Blob_sender_obj is not Null_peer (S_HAS_BLOB_PIPE).

Returns
See above.

◆ end_sending()

Performs hndl_snd()-> and/or blob_snd()-> same method, returning true if all (1-2) invoked methods returned true; false conversely.

As with async_end_sending() (either overload) behavior is undefined (assertion may trip), if one returned true and the other false.

Returns
See above.

◆ hndl_rcv() [1/2]

Pointer to the mutable owned Native_handle_receiver_obj; null if not yet initialized.

Compilable only if Native_handle_receiver_obj is not Null_peer (S_HAS_NATIVE_HANDLE_PIPE).

Returns
See above.

◆ hndl_rcv() [2/2]

Pointer to the immutable owned Native_handle_receiver_obj; null if not yet initialized.

Compilable only if Native_handle_receiver_obj is not Null_peer (S_HAS_NATIVE_HANDLE_PIPE).

Returns
See above.

◆ hndl_snd() [1/2]

Pointer to the mutable owned Native_handle_sender_obj; null if not yet initialized.

Compilable only if Native_handle_sender_obj is not Null_peer (S_HAS_NATIVE_HANDLE_PIPE).

Returns
See above.

◆ hndl_snd() [2/2]

Pointer to the immutable owned Native_handle_sender_obj; null if not yet initialized.

Compilable only if Native_handle_sender_obj is not Null_peer (S_HAS_NATIVE_HANDLE_PIPE).

Returns
See above.

◆ idle_timer_run()

template<typename Blob_sender , typename Blob_receiver , typename Native_handle_sender , typename Native_handle_receiver >
bool ipc::transport::Channel< Blob_sender, Blob_receiver, Native_handle_sender, Native_handle_receiver >::idle_timer_run ( util::Fine_duration  timeout = boost::chrono::seconds(5))

Performs hndl_rcv()-> and/or blob_rcv()-> same method, returning true if all (1-2) invoked methods returned true; false conversely.

As with async_end_sending() (either overload) behavior is undefined (assertion may trip), if one returned true and the other false.

Parameters
timeoutSee above.
Returns
See above.

◆ init_blob_pipe() [1/2]

Completes initialization of the blobs pipe by taking ownership (via move semantics) of separate Blob_sender_obj and Blob_receiver_obj objects for our end of the blobs pipe.

Call this 0 times if blobs pipe disabled (in which case Blob_sender_obj and Blob_receiver_obj should both be Null_peer). Call either this or the 1-arg overload exactly 1 time otherwise.

Certain mistaken uses are caught in this method; it no-ops and returns false (failure):

The remaining mistakes are caught by initialized(), if you choose to call it before any transmission (and you should):

Pre-condition: snd and rcv are in PEER state. Behavior is undefined otherwise. Post-condition: The *this blobs pipe is in PEER state (transmission can begin).

Informal recommendation: Complete the handles pipe initialization, if enabled, before transmitting anything over the blobs pipe, even though technically it is possible to do so immediately. Use initialized() to double-check.

Note
As a user, it is likely you can/should use an alias type that will take care of calling this for you. See Channel doc header.
Parameters
sndA Blob_sender_obj in PEER state.
rcvA Blob_receiver_obj in PEER state.
Returns
true on success; false on no-op due to a mistaken use listed above.

◆ init_blob_pipe() [2/2]

Completes initialization of the blobs pipe by taking ownership (via move semantics) of an object that is simultaneously the Blob_sender_obj and Blob_receiver_obj for our end of the blobs pipe.

Call this 0 times (successfully) if blobs pipe disabled (in which case Blob_sender_obj and Blob_receiver_obj should both be Null_peer). Call either this or the 2-arg overload exactly 1 time (successfully) otherwise. If you call this,

Blob_sender_obj and Blob_receiver_obj must be the same type.

Certain mistaken uses are caught in this method; it no-ops and returns false (failure):

The remaining mistakes are caught by initialized(), if you choose to call it before any transmission (and you should):

Pre-condition: snd_and_rcv is in PEER state. Behavior is undefined otherwise. Post-condition (on success): The *this blobs pipe is in PEER state (transmission can begin).

Informal recommendation: Complete the handles pipe initialization, if enabled, before transmitting anything over the blobs pipe, even though technically it is possible to do so immediately. Use initialized() to double-check.

Warning
It is an error (as noted above) to call this, unless Blob_sender_obj and Blob_receiver_obj are the same type. This will no-op and return false. The only reason it is not undefined behavior (assertion trip) is so that this mistake can be caught without an assert(), if you choose to call initialized() after the init phase yourself.
Note
As a user, it is likely you can/should use an alias type that will take care of calling this for you. See Channel doc header.
Parameters
snd_and_rcvA Blob_sender_obj and Blob_receiver_obj in PEER state.
Returns
true on success; false on no-op due to a mistaken use listed above.

◆ init_native_handle_pipe() [1/2]

Analogous to 2-arg init_blob_pipe() but as applied to the handles pipe.

All its notes apply by analogy.

Parameters
sndA Native_handle_sender_obj in PEER state.
rcvA Native_handle_receiver_obj in PEER state.
Returns
See init_blob_pipe().

◆ init_native_handle_pipe() [2/2]

template<typename Blob_sender , typename Blob_receiver , typename Native_handle_sender , typename Native_handle_receiver >
bool ipc::transport::Channel< Blob_sender, Blob_receiver, Native_handle_sender, Native_handle_receiver >::init_native_handle_pipe ( Native_handle_sender_obj &&  snd_and_rcv)

Analogous to 1-arg init_blob_pipe() but as applied to the handles pipe.

All its notes apply by analogy.

Parameters
snd_and_rcvA Native_handle_sender_obj and Native_handle_receiver_obj in PEER state.
Returns
See init_blob_pipe().

◆ initialized()

template<typename Blob_sender , typename Blob_receiver , typename Native_handle_sender , typename Native_handle_receiver >
bool ipc::transport::Channel< Blob_sender, Blob_receiver, Native_handle_sender, Native_handle_receiver >::initialized ( bool  suppress_log = false) const

Returns true if and only if the required init_blob_pipe() and init_native_handle_pipe() calls have been made, loading exactly the expected peer objects as the template params Blob_sender_obj, Blob_receiver_obj, Native_handle_sender_obj, Native_handle_receiver_obj specify.

This is useful particularly when meta-programming on top of Channel: one can call initialized() before any transmission methods to ensure the proper init_*() calls were made. After that it is safe to meta-program in terms of just the compile-time values S_HAS_2_PIPES, S_HAS_BLOB_PIPE, S_HAS_NATIVE_HANDLE_PIPE, S_HAS_BLOB_PIPE_ONLY, S_HAS_NATIVE_HANDLE_PIPE_ONLY, S_IS_SYNC_IO_OBJ, S_IS_ASYNC_IO_OBJ.

Namely it will check that:

If this returns false, formally, behavior is undefined, if one attempts transmission. Informally, likely some intended-for-use transmission methods will always return false and no-op; but the best recommendation is: If this returns false, do not use *this for transmission.

Parameters
suppress_logIf and only if true, returning false shall not be explained via logging. This may be useful when one merely wants to check whether *this is in default-cted state without implying that's a terrible thing.
Returns
See above.

◆ nickname()

Returns nickname, a brief string suitable for logging.

This is included in the output by the ostream<< operator as well. This method is thread-safe in that it always returns the same value.

If this object is default-cted (or moved-from), this will return a value equal to "".

Returns
See above.

◆ operator=()

Move-assigns from src; *this acts as if destructed; src becomes as-if default-cted (therefore in ??? state).

No-op if &src == this.

See also
~Channel().
Parameters
srcSee above.
Returns
*this.

◆ receive_blob_max_size()

Yields blob_rcv()-> same method.

Returns
See concept API.

◆ receive_meta_blob_max_size()

template<typename Blob_sender , typename Blob_receiver , typename Native_handle_sender , typename Native_handle_receiver >
size_t ipc::transport::Channel< Blob_sender, Blob_receiver, Native_handle_sender, Native_handle_receiver >::receive_meta_blob_max_size

Yields hndl_rcv()-> same method.

Returns
See concept API.

◆ replace_event_wait_handles()

template<typename Blob_sender , typename Blob_receiver , typename Native_handle_sender , typename Native_handle_receiver >
template<typename Create_ev_wait_hndl_func >
bool ipc::transport::Channel< Blob_sender, Blob_receiver, Native_handle_sender, Native_handle_receiver >::replace_event_wait_handles ( const Create_ev_wait_hndl_func &  create_ev_wait_hndl_func)

Executes same method on all unique stored peer objects; returns true if and only if they all did.

(initialized() must be true; else behavior undefined.)

Compilable only if S_IS_SYNC_IO_OBJ.

Template Parameters
Create_ev_wait_hndl_funcSee concept API.
Parameters
create_ev_wait_hndl_funcSee concept API.
Returns
See above.

◆ send_blob()

template<typename Blob_sender , typename Blob_receiver , typename Native_handle_sender , typename Native_handle_receiver >
bool ipc::transport::Channel< Blob_sender, Blob_receiver, Native_handle_sender, Native_handle_receiver >::send_blob ( const util::Blob_const blob,
Error_code err_code = 0 
)

Yields blob_snd()-> same method.

Parameters
blobSee concept API.
err_codeSee concept API.
Returns
See concept API.

◆ send_blob_max_size()

Yields blob_snd()-> same method.

Returns
See concept API.

◆ send_meta_blob_max_size()

Yields hndl_snd()-> same method.

Returns
See concept API.

◆ send_native_handle()

template<typename Blob_sender , typename Blob_receiver , typename Native_handle_sender , typename Native_handle_receiver >
bool ipc::transport::Channel< Blob_sender, Blob_receiver, Native_handle_sender, Native_handle_receiver >::send_native_handle ( Native_handle  hndl_or_null,
const util::Blob_const meta_blob,
Error_code err_code = 0 
)

Yields hndl_snd()-> same method.

Parameters
hndl_or_nullSee concept API.
meta_blobSee concept API.
err_codeSee concept API.
Returns
See concept API.

◆ start_receive_blob_ops()

template<typename Blob_sender , typename Blob_receiver , typename Native_handle_sender , typename Native_handle_receiver >
template<typename Event_wait_func_t >
bool ipc::transport::Channel< Blob_sender, Blob_receiver, Native_handle_sender, Native_handle_receiver >::start_receive_blob_ops ( Event_wait_func_t &&  ev_wait_func)

Yields blob_rcv()-> same method.

Template Parameters
Event_wait_func_tSee concept API.
Parameters
ev_wait_funcSee concept API.
Returns
See concept API.

◆ start_receive_native_handle_ops()

template<typename Blob_sender , typename Blob_receiver , typename Native_handle_sender , typename Native_handle_receiver >
template<typename Event_wait_func_t >
bool ipc::transport::Channel< Blob_sender, Blob_receiver, Native_handle_sender, Native_handle_receiver >::start_receive_native_handle_ops ( Event_wait_func_t &&  ev_wait_func)

Yields hndl_rcv()-> same method.

Template Parameters
Event_wait_func_tSee concept API.
Parameters
ev_wait_funcSee concept API.
Returns
See concept API.

◆ start_send_blob_ops()

template<typename Blob_sender , typename Blob_receiver , typename Native_handle_sender , typename Native_handle_receiver >
template<typename Event_wait_func_t >
bool ipc::transport::Channel< Blob_sender, Blob_receiver, Native_handle_sender, Native_handle_receiver >::start_send_blob_ops ( Event_wait_func_t &&  ev_wait_func)

Yields blob_snd()-> same method.

Template Parameters
Event_wait_func_tSee concept API.
Parameters
ev_wait_funcSee concept API.
Returns
See concept API.

◆ start_send_native_handle_ops()

template<typename Blob_sender , typename Blob_receiver , typename Native_handle_sender , typename Native_handle_receiver >
template<typename Event_wait_func_t >
bool ipc::transport::Channel< Blob_sender, Blob_receiver, Native_handle_sender, Native_handle_receiver >::start_send_native_handle_ops ( Event_wait_func_t &&  ev_wait_func)

Yields hndl_snd()-> same method.

Template Parameters
Event_wait_func_tSee concept API.
Parameters
ev_wait_funcSee concept API.
Returns
See concept API.

Friends And Related Function Documentation

◆ operator<<()

template<typename Blob_sender , typename Blob_receiver , typename Native_handle_sender , typename Native_handle_receiver >
std::ostream & operator<< ( std::ostream &  os,
const Channel< Blob_sender, Blob_receiver, Native_handle_sender, Native_handle_receiver > &  val 
)
related

Prints string representation of the given Channel to the given ostream.

Parameters
osStream to which to write.
valObject to serialize.
Returns
os.

Member Data Documentation

◆ S_IS_ASYNC_IO_OBJ

template<typename Blob_sender , typename Blob_receiver , typename Native_handle_sender , typename Native_handle_receiver >
constexpr bool ipc::transport::Channel< Blob_sender, Blob_receiver, Native_handle_sender, Native_handle_receiver >::S_IS_ASYNC_IO_OBJ = !S_IS_SYNC_IO_OBJ
staticconstexpr

It equals the reverse of S_IS_SYNC_IO_OBJ.

That is all peer types are of the async-I/O pattern variety (by convention living directly in namespace ipc::transport).


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