Flow-IPC 3.0.0
Flow-IPC project: Public API.
Classes | Typedefs | Functions
ipc::transport::sync_io Namespace Reference

sync_io-pattern counterparts to async-I/O-pattern object types in parent namespace ipc::transport. More...

Classes

class  Async_adapter_receiver
 Type that adapts a given PEER-state sync_io::Native_handle_receiver or sync_io::Blob_receiver core into the async-I/O-pattern Native_handle_receiver or Blob_receiver. More...
 
class  Async_adapter_sender
 Type that adapts a given PEER-state sync_io::Native_handle_sender or sync_io::Blob_sender core into the async-I/O-pattern Native_handle_sender or Blob_sender. More...
 
class  Blob_receiver
 A documentation-only concept: what transport::Blob_receiver is to transport::Native_handle_receiver (namely a degenerate version thereof), this is to sync_io::Native_handle_receiver. More...
 
class  Blob_sender
 A documentation-only concept: what transport::Blob_sender is to transport::Native_handle_sender (namely a degenerate version thereof), this is to sync_io::Native_handle_sender. More...
 
class  Blob_stream_mq_receiver
 Implements sync_io::Blob_receiver concept by using an adopted Persistent_mq_handle MQ handle to an MQ (message queue) of that type, such as a POSIX or bipc MQ. More...
 
class  Blob_stream_mq_sender
 Implements sync_io::Blob_sender concept by using an adopted Persistent_mq_handle MQ handle to an MQ (message queue) of that type, such as a POSIX or bipc MQ. More...
 
class  Native_handle_receiver
 A documentation-only concept defining the behavior of an object that is the sync_io-pattern counterpart of the async-I/O-pattern-following concept of the same name in our parent namespace: transport::Native_handle_receiver. More...
 
class  Native_handle_sender
 A documentation-only concept defining the behavior of an object that is the sync_io-pattern counterpart of the async-I/O-pattern-following concept of the same name in our parent namespace: transport::Native_handle_sender. More...
 
class  Native_socket_stream
 Implements both sync_io::Native_handle_sender and sync_io::Native_handle_receiver concepts by using a Unix domain socket, allowing high-performance but non-zero-copy transmission of discrete messages, each containing a native handle, a binary blob, or both. More...
 
class  Native_socket_stream_acceptor
 sync_io-pattern counterpart to async-I/O-pattern transport::Native_socket_stream_acceptor. More...
 

Typedefs

using Posix_mq_sender = Blob_stream_mq_sender< Posix_mq_handle >
 Convenience alias: sync_io::Blob_sender via unidirectional POSIX MQ (message queue).
 
using Posix_mq_receiver = Blob_stream_mq_receiver< Posix_mq_handle >
 Convenience alias: sync_io::Blob_receiver via unidirectional POSIX MQ (message queue).
 
using Bipc_mq_sender = Blob_stream_mq_sender< Bipc_mq_handle >
 Convenience alias: sync_io::Blob_sender via unidirectional bipc MQ (message queue).
 
using Bipc_mq_receiver = Blob_stream_mq_receiver< Bipc_mq_handle >
 Convenience alias: sync_io::Blob_receiver via unidirectional bipc MQ (message queue).
 
using Posix_mqs_channel_of_blobs = transport::Mqs_channel< true, Posix_mq_handle >
 Convenience alias: Channel peer (sync_io::Blob_sender, sync_io::Blob_receiver) at one end of full-duplex (bidirectional) pipe composed of 2 opposite-facing unidirectional POSIX MQs (message queues).
 
using Bipc_mqs_channel_of_blobs = transport::Mqs_channel< true, Bipc_mq_handle >
 Convenience alias: Channel peer (sync_io::Blob_sender, sync_io::Blob_receiver) at one end of full-duplex (bidirectional) pipe composed of 2 opposite-facing unidirectional POSIX MQs (message queues).
 
using Posix_mqs_socket_stream_channel = Mqs_socket_stream_channel< true, Posix_mq_handle >
 Convenience alias: Channel peer (sync_io::Blob_sender, sync_io::Blob_receiver, sync_io::Native_handle_sender, sync_io::Native_handle_receiver) at one end of a full-duplex (bidirectional) pipe composed of 2 opposite-facing unidirectional POSIX MQs (message queues), transmitting blobs only; and a full-duplex pipe over a Unix domain stream connection, transmitting native-handle-and/or-meta-blob messages.
 
using Bipc_mqs_socket_stream_channel = Mqs_socket_stream_channel< true, Bipc_mq_handle >
 Convenience alias: Channel peer (sync_io::Blob_sender, sync_io::Blob_receiver, sync_io::Native_handle_sender, sync_io::Native_handle_receiver) at one end of a full-duplex (bidirectional) pipe composed of 2 opposite-facing unidirectional bipc MQs (message queues), transmitting blobs only; and a full-duplex pipe over a Unix domain stream connection, transmitting native-handle-and/or-meta-blob messages.
 

Functions

template<bool NO_HNDLS, typename Batch , typename Task_err , typename Async_rcv_impl_func >
void async_receive_batch_emulation (flow::log::Logger *logger_ptr, Batch *batch, bool assume_would_block, Error_code *sync_err_code, Task_err &&on_done_func, Async_rcv_impl_func &&async_rcv_impl_func)
 Implements {Native_handle|Blob}_receiver::async_receive_*_batch() by emulating batch-receiving as a series of single-message receive-ops. More...
 
template<typename Persistent_mq_handle >
std::ostream & operator<< (std::ostream &os, const Blob_stream_mq_receiver< Persistent_mq_handle > &val)
 Prints string representation of the given Blob_stream_mq_receiver to the given ostream. More...
 
template<typename Persistent_mq_handle >
std::ostream & operator<< (std::ostream &os, const Blob_stream_mq_sender< Persistent_mq_handle > &val)
 Prints string representation of the given Blob_stream_mq_sender to the given ostream. More...
 
std::ostream & operator<< (std::ostream &os, const Native_socket_stream &val)
 Prints string representation of the given Native_socket_stream to the given ostream. More...
 
std::ostream & operator<< (std::ostream &os, const Native_socket_stream_acceptor &val)
 Prints string representation of the given Native_socket_stream_acceptor to the given ostream. More...
 

Detailed Description

sync_io-pattern counterparts to async-I/O-pattern object types in parent namespace ipc::transport.

For example transport::sync_io::Native_socket_stream <=> transport::Native_socket_stream.

See also
util::sync_io doc header – describes the general sync_io pattern we are following.

Function Documentation

◆ async_receive_batch_emulation()

template<bool NO_HNDLS, typename Batch , typename Task_err , typename Async_rcv_impl_func >
void ipc::transport::sync_io::async_receive_batch_emulation ( flow::log::Logger *  logger_ptr,
Batch *  batch,
bool  assume_would_block,
Error_code *  sync_err_code,
Task_err &&  on_done_func,
Async_rcv_impl_func &&  async_rcv_impl_func 
)

Implements {Native_handle|Blob}_receiver::async_receive_*_batch() by emulating batch-receiving as a series of single-message receive-ops.

How to use

Just call us; we'll do it. The main requirement is to provide a proper async_rcv_impl_func() which shall perform a regular one-message receive.

The formal requirements for async_rcv_impl_func() are as follows. It shall be called in a void context and have the following arguments, in order:

  • (If and only if NO_HNDLS == false) Native_handle*: Target handle object.
  • bool: If true, your function may assume the pipe is in would-block state already which may help it be more efficient in doing its task; otherwise it must make no such assumption. (This will not simply always equal the eponymous argument to async_receive_batch_emulation()!)
  • util::Blob_mutable: Target memory area for the async-read.
  • Error_code*: The error-code object for the op. This shall not be null (you do not need to throw an exception to emit an error).
  • size_t*: Set the pointee to the received in-blob's size, unless an error is emitted.
  • Function-object of the specific type flow::async::Task_asio_err_sz, a/k/a Function<void (const Error_code& err_code, size_t n_rcvd)>. Attention! This may, or may not, be .empty(). See below.

It shall act as-if {Native_handle|Blob}_receiver::async_receive_{native_handle|blob}() was called, except:

  • Which one it is: it must act consistently with NO_HNDLS.
    • It shall take the extra leading Native_handle* arg (see above) if NO_HNDLS == false.
  • It may assume the following checks have all passed: in PEER state, start_*_ops() has been called, no pipe-hosing error recorded yet from prior ops, no async_receive_*() already outstanding, batch->initialized() && (!batch->full()).
  • May act differently (probably for perf savings) depending on the bool arg (assume-would-block; see above).
  • If it emits non-success, non-would-block (in-pipe-hosing) error E, and the desired outcome if 1+ in-messages have already been received successfully into *batch is delayed error (emit no error for this batch-receive, but next async-receive on that in-pipe shall instantly yield E), then E must be one of the following:
    • error::Code::S_RECEIVES_FINISHED_CANNOT_RECEIVE,
    • boost::asio::error::eof.
    • Any other E in that situation will cause the batch-receive to emit E (and therefore leave batch->n_used() unchanged); 1+ in-messages shall be eaten. (Promise: any Native_handles received with the eaten in-messages shall be closed – returned to the OS – not leaked.)
  • The on-done handler may be non-empty (as required for normal user-triggered calls) or .empty(). If it's non-empty, act normally. If it's empty, and no would-block is encountered, act normally; which is to say synchronously emit the result, and that's that (on-done handler ignored). If it's empty, and would-block is encountered then there are 2 possibilities.
    • If would-block is encountered immediately (no partial message is read/non-steady state reached): Emit would-block as normal; but end the async-receive op. Do not issue an async-wait for readability; do not save on-done handler (which is empty anyway).
    • If would-block is encountered after receiving part of an in-message/a non-steady state is reached: Do the same; plus:
      • Memorize the partial-message payload/non-steady state, so that if another async-receive is issued by something/someone later, your state machine starts from the point as-if the partial-message payload/state had already been received/reached. (After all: it had already been received/reached – it just ended up irrelevant to the previous async-receive; namely us.)

Unfortunately that last bullet point can be tricky to implement (or even understand what it means), so we must discuss. Firstly the (potential) good news: If your protocol combined with the underlying transport is such that reading a partial in-message is impossible, and (non-hosed) pipe state is the same regardless of where a would-block occurs, then the bullet point cannot apply, and things (your code) remain simple.

Example: Native_socket_stream, when operating (and this is determined at compile-time, not run-time) in datagram mode (asio_local_stream_socket::Protocol_pkt_stream, not Protocol_byte_stream; a/k/a in Linux SOCK_SEQPACKET, not SOCK_STREAM) can (and in our impl as of this writing does) map each in-message to one in-datagram, and the pipe is always in the same state (barring being hosed) upon receiving any in-dgram.

However if it possible to read a payload which encodes (potentially) part of an in-message/non-steady state, then the bullet point may apply, and you must code for it.

Example: Blob_stream_mq_receiver is essentially dgram-based too, so each in-message maps to one lower-level dgram (MQ message), but as of this writing our internal protocol is (for certain boring logical/technical reasons) such that some (non-user-in-message) messages are represented by 2 dgrams (MQ messages), not 1: a CONTROL message like an auto-ping is represented by an empty in-message (enters CONTROL state) and then a particular enumeration-value-encoding message (indicates auto-ping; goes back to normal state). So, the start of async-receive cannot assume normal state; it might be in CONTROL state because of the above. Of course this is pretty easy to handle; just keep a CONTROL-or-not state flag; and resume the state machine based on its value. There's no need to store any user payload copy.

Example: Native_socket_stream, when operating in stream mode (asio_local_stream_socket::Protocol_byte_stream a/k/a SOCK_STREAM unlike the earlier example) might hit would-block after any given byte whatsoever; right down to the "worst case" of, say, reading all of a 60K-long payload except for the very last expected byte. Then you'd have to copy it from the would-be user buffer; and feed that part to the next async-receive's user buffer (another copy). Fortunately this eventuality should not be frequent enough to affect overall perf.

In that case you'll need to do somehow save any partial-read results and apply them to the next async-read of any type at the start of that async-read, before reading any further potential low-level data. Usually this isn't a matter of performance, as at worst it's part of only 1 in-message, but you'll need some extra code, and the support for emulated batch-receiving will impact code not-itself-necessarily-related to batch-receiving.

Warning
It is important that async_rcv_impl_func() cache any new pipe-hosing error it encounters (and therefore emits via Error_code* – but note it may emit non-pipe-hosing error(s) too, most notably would-block; that is different); so that if an async-receive is attempted subsequently, it knows to immediately emit it. We mention this, because async_receive_batch_emulation() will – in the case of encountering 1+ in-messages followed by error (e.g., graceful close error::Code::S_RECEIVES_FINISHED_CANNOT_RECEIVE) – only emit the 1+ messages and not any error; it will stop but ignore the error itself. {Native_handle|Blob}_receiver::async_receive_*() contract is to emit the error next time in that case – and async_receive_batch_emulation() shall take no steps of its own to make that happen. Your async_rcv_impl_func() must take care of that.

Rationale / use-cases

See also
Native_handle_receiver concept doc header "Batch-receiving" section for background.

As of this writing Native_socket_stream and Blob_stream_mq_receiver use it internally. The latter does so, since (as of now anyway) there is no built-in batch-receiving OS support for POSIX MQs (nor bipc MQs). The former does so if and only if Native_socket_stream_cfg::S_USE_OS_DGRAM_BATCH_SUPPORT is false. In English – in both cases there's no low-level batch-receiving, so to provide that higher-level interface it has to be emulated based on normal receiving.

The same task would be faced by any potential Blob_receiver or Native_handle_receiver concept implementer, including when a Flow-IPC user wants to do so; an advanced type of work but entirely supported. Therefore it would be relatively rare that a user would call this... but definitely possible. Hence it is public.

Template Parameters
NO_HNDLStrue if implementing Blob_receiver::async_receive_blob_batch(). false if Native_handle_receiver::async_receive_native_handle_batch().
BatchBlob_receiver::Blob_batch_in or Native_handle_receiver::Native_handle_batch_in (see NO_HNDLS). In addition, it must have the APIs with identical names/semantics to Generic_msg_batch_in::clear_used(size_t), Generic_msg_batch_in::emulate_result(), Generic_msg_batch_in::next_target_blob(), and – if NO_HNDLS == false – Generic_msg_batch_in::next_target_hndl(). (Rationale: We could have instead simply required that Batch be an instance of Generic_msg_batch_in<Msg_resource, NO_HNDLS>. Decided to formally allow any type, as long as it has the required behavior – for freedom in custom scenarios.)
Task_errAs for {Native_handle|Blob}_receiver::async_receive_*_batch().
Async_rcv_impl_funcSee above.
Parameters
logger_ptrLogger to use for logging in this op, including its async-continuation if applicable.
batchAs for {Native_handle|Blob}_receiver::async_receive_*_batch().
assume_would_blockAs for {Native_handle|Blob}_receiver::async_receive_*_batch().
sync_err_codeAs for {Native_handle|Blob}_receiver::async_receive_*_batch().
on_done_funcAs for {Native_handle|Blob}_receiver::async_receive_*_batch().
async_rcv_impl_funcYour *_receiver's single-message receive op. See above.

◆ operator<<() [1/4]

template<typename Persistent_mq_handle >
std::ostream & operator<< ( std::ostream &  os,
const Blob_stream_mq_receiver< Persistent_mq_handle > &  val 
)

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

If object is default-cted (or moved-from), this will output something graceful indicating this.

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

◆ operator<<() [2/4]

template<typename Persistent_mq_handle >
std::ostream & operator<< ( std::ostream &  os,
const Blob_stream_mq_sender< Persistent_mq_handle > &  val 
)

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

If object is default-cted (or moved-from), this will output something graceful indicating this.

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

◆ operator<<() [3/4]

std::ostream & operator<< ( std::ostream &  os,
const Native_socket_stream &  val 
)

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

Friend of Native_socket_stream.

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

◆ operator<<() [4/4]

std::ostream & operator<< ( std::ostream &  os,
const Native_socket_stream_acceptor &  val 
)

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

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