Flow-IPC 1.0.0
Flow-IPC project: Public API.
|
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... | |
Channel & | operator= (Channel &&src) |
Move-assigns from src ; *this acts as if destructed; src becomes as-if default-cted (therefore in ??? state). More... | |
Channel & | operator= (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_obj * | blob_snd () const |
Pointer to the immutable owned Blob_sender_obj; null if yet initialized. More... | |
const Blob_receiver_obj * | blob_rcv () const |
Pointer to the immutable owned Blob_receiver_obj; null if not yet initialized. More... | |
const Native_handle_sender_obj * | hndl_snd () const |
Pointer to the immutable owned Native_handle_sender_obj; null if not yet initialized. More... | |
const Native_handle_receiver_obj * | hndl_rcv () const |
Pointer to the immutable owned Native_handle_receiver_obj; null if not yet initialized. More... | |
Blob_sender_obj * | blob_snd () |
Pointer to the mutable owned Blob_sender_obj; null if yet initialized. More... | |
Blob_receiver_obj * | blob_rcv () |
Pointer to the mutable owned Blob_receiver_obj; null if not yet initialized. More... | |
Native_handle_sender_obj * | hndl_snd () |
Pointer to the mutable owned Native_handle_sender_obj; null if not yet initialized. More... | |
Native_handle_receiver_obj * | hndl_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... | |
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 Channel
s 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 Channel
s. (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.)
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:
Blob_sender
/Blob_receiver
, which may be the same object or not; or Native_handle_sender
/Native_handle_receiver
, ditto; or both);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.
Channel::S_IS_SYNC_IO_OBJ == true
unstructured Channel in every constructor form offered.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.
transport::
and transport::sync_io::
sender/receiver concepts, each method simply forwards to the appropriate stored peer object. Therefore they can be seen as simple syntactic sugar – it is really just forwarding.
async_end_sending(F)
invokes completion handler F()
only once all (1-2) out-pipes have finished (this may save you code having to worry about it).S_IS_SYNC_IO_OBJ
and S_IS_ASYNC_IO_OBJ
Channel
formsIn addition the following transport::sync_io::
methods are available as syntactic sugar if and only if S_IS_SYNC_IO_OBJ
.
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:
Blob_*er
and Native_handle_*er
at construction time when specifying the concrete Channel type.sync_io::
qualifier below where applicable.Native_socket_stream
; Blob_sender can also be Blob_stream_mq_sender<Mq>
(or the specific aliases Posix_mq_sender
, Bipc_mq_sender
); Blob_receiver
can be Blob_stream_mq_receiver<Mq>
(aliases Posix_mq_receiver
, Bipc_mq_receiver
).Channel<Posix_mq_sender, Posix_mq_receiver, Null_peer, Null_peer>
: Blob pipe via 2 unidirectional POSIX MQs; handles pipe disabled.Channel<Null_peer, Null_peer, Native_socket_stream, Native_socket_stream>
: Blob pipe disabled; handles pipe over a Unix domain stream socket connection.Channel<Null_peer, Null_peer, sync_io::Native_socket_stream, sync_io::Native_socket_stream>
: Same but bundling sync_io
cores instead of async-I/O counterparts.true
. Otherwise you've failed to follow the above instructions. This may be reason enough to assert(false)
or abort, but it's up to you. In any case it is unwise, at best, to proceed with anything below.After this, initialization is finished. The Channel is in PEER state. One can use these approaches:
*this
itself.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.
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:
SIO
determines whether Native_socket_stream or sync_io::Native_socket_stream).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 Channel
s 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
.
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:
*this
: it can now transmit.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:
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:
*this
Channel.async_end_sending()
. If it returns false
, or once the completion handler executes (or the sync_io
form synchronously succeeds):*this
Channel (to avoid unnecessary resource use including background threads in the async-I/O-pattern case).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.
Blob_sender | Implements 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_receiver | Implements 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_sender | Implements 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_receiver | Implements 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:: . |
ipc::transport::Channel< Blob_sender, Blob_receiver, Native_handle_sender, Native_handle_receiver >::Channel |
Default ctor (Channel is in ??? state; intended to be move-assigned).
This ctor is informally intended for the following uses:
src
arg move-ctor and move-assignment operator) becomes as-if defaulted-constructed.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.
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:
*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.
logger_ptr | Logger to use for subsequently logging. |
nickname_str | Human-readable nickname of the new object, as of this writing for use in operator<<(ostream) and logging only. |
default |
Move-constructs from src
; src
becomes as-if default-cted (therefore in ??? state).
src | See above. |
ipc::transport::Channel< Blob_sender, Blob_receiver, Native_handle_sender, Native_handle_receiver >::~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.
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:
on_done_func()
is invoked (exactly once) if and only if both forwarded methods have completed.Error_code
passed to on_done_func()
is:Error_code
from the failed completion;Error_code
from the first failed completion."Completion" in this case means the first (and only) one to occur for each pipe:
.async_end_sending()
yielded synchronous (immediate) completion as opposed to error::Code::S_SYNC_IO_WOULD_BLOCK. Or:sync_io
-pattern machinery) its completion handler executed.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.
Task_err | See concept API. |
sync_err_code | See 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_func | See above. |
true
if both returned true
; false
if both returned false
; behavior undefined if they returned conflicting values. 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:
on_done_func()
is invoked (exactly once) if and only if both forwarded methods have completed.Error_code
passed to on_done_func()
is:Error_code
from the failed completion;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.
Task_err | See concept API. |
on_done_func | See above. |
true
if both returned true
; false
if both returned false
; behavior undefined if they returned conflicting values. 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.
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.
Args | See above. |
args | See concept API. |
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.
Args | See above. |
args | See concept API. |
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
.
period | See concept API. |
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).
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).
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).
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).
bool ipc::transport::Channel< Blob_sender, Blob_receiver, Native_handle_sender, Native_handle_receiver >::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
.
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).
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).
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).
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).
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
.
timeout | See above. |
bool ipc::transport::Channel< Blob_sender, Blob_receiver, Native_handle_sender, Native_handle_receiver >::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.
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.
snd | A Blob_sender_obj in PEER state. |
rcv | A Blob_receiver_obj in PEER state. |
true
on success; false
on no-op due to a mistaken use listed above. bool ipc::transport::Channel< Blob_sender, Blob_receiver, Native_handle_sender, Native_handle_receiver >::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.
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,
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.
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.snd_and_rcv | A Blob_sender_obj and Blob_receiver_obj in PEER state. |
true
on success; false
on no-op due to a mistaken use listed above. bool ipc::transport::Channel< Blob_sender, Blob_receiver, Native_handle_sender, Native_handle_receiver >::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.
All its notes apply by analogy.
snd | A Native_handle_sender_obj in PEER state. |
rcv | A Native_handle_receiver_obj in PEER state. |
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.
snd_and_rcv | A Native_handle_sender_obj and Native_handle_receiver_obj in PEER state. |
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.
suppress_log | If 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. |
const std::string & ipc::transport::Channel< Blob_sender, Blob_receiver, Native_handle_sender, Native_handle_receiver >::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 "".
default |
Move-assigns from src
; *this
acts as if destructed; src
becomes as-if default-cted (therefore in ??? state).
No-op if &src == this
.
src | See above. |
*this
. size_t ipc::transport::Channel< Blob_sender, Blob_receiver, Native_handle_sender, Native_handle_receiver >::receive_blob_max_size |
Yields blob_rcv()->
same method.
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.
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.
Create_ev_wait_hndl_func | See concept API. |
create_ev_wait_hndl_func | See concept API. |
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.
blob | See concept API. |
err_code | See concept API. |
size_t ipc::transport::Channel< Blob_sender, Blob_receiver, Native_handle_sender, Native_handle_receiver >::send_blob_max_size |
Yields blob_snd()->
same method.
size_t ipc::transport::Channel< Blob_sender, Blob_receiver, Native_handle_sender, Native_handle_receiver >::send_meta_blob_max_size |
Yields hndl_snd()->
same method.
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.
hndl_or_null | See concept API. |
meta_blob | See concept API. |
err_code | See concept API. |
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.
Event_wait_func_t | See concept API. |
ev_wait_func | See concept API. |
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.
Event_wait_func_t | See concept API. |
ev_wait_func | See concept API. |
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.
Event_wait_func_t | See concept API. |
ev_wait_func | See concept API. |
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.
Event_wait_func_t | See concept API. |
ev_wait_func | See concept API. |
|
related |
Prints string representation of the given Channel
to the given ostream
.
os | Stream to which to write. |
val | Object to serialize. |
os
.
|
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).