Asio in amongoc
#
Warning
This page is for amongoc
developers and the documented components and
behavior are not part of any public API guarantees.
This page will explain the basics of Asio and how it is used internally in
amongoc
.
Asio vs Boost.Asio#
Asio comes in two flavors: Boost, and non-Boost (i.e. “Standalone”).
Boost.Asio is an automatically-converted version of Asio that has all APIs moved
from ::asio
to the ::boost::asio
namespace, and makes use of some Boost
libraries in place of standard library components. The following differences are
of note:
All components in
::asio::
are in::boost::asio::
Asio accepts an
asio::error_code
, which is a typedef tostd::error_code
in standalone Asio, and a typedef ofboost::system::error_code
in Boost.Asio.[1]
The Asio Asynchrony Model#
Asio asynchrony is created using asynchronous APIs that accept an object known
as a CompletionToken
, which informs the operation how
to initiate and how to complete.
Asio’s async API’s can be further broken down into two classes: I/O objects, and asynchronous algorithms.
I/O Objects#
Asio I/O objects are objects that abstract the implementaion of an underlying
system resource that performs asynchronous (or synchronous) operations. The
low-level Asio I/O objects must be associated with an asio::io_context
.
The Asio I/O objects are only used within amongoc
’s default event loop
implementation.
Asynchronous Algorithms#
Asio’s asynchronous algorithms are extremely powerful APIs that do not rely
on a particular event loop or any particular I/O object implementation. Instead,
the algorithms are written generically in terms of asynchronous object concepts,
such as asio::AsyncReadStream
and asio::AsyncWriteStream
. As long as the
operands to the algorithms meet the generic concept requirements, the algorithms
will Just Work™.
The Asio algorithms are used as the basis for the wire protocol implementation
in amongoc
. The amongoc::tcp_connection_rw_stream
type implements
asio::AsyncReadStream
and asio::AsyncWriteStream
, so it can be used for any
Asio algorithm that expects such an interface.
APIs of Note#
Asio APIs#
-
class asio::io_context#
This is the default Asio event loop type. I/O objects are created and associated with an
io_context
.In
amongoc
,io_context
is used as the backing implementation of thedefault event loop
.At no other place in
amongoc
isasio::io_context
used for any purpose.amongoc
allows the user to provide their own event loop, so we cannot assume that we have an Asioio_context
available.
-
template<typename T>
concept asio::CompletionToken# This is an exposition-only concept from Asio for CompletionToken objects.
A CompletionToken is an object that tells an asynchronous operation API function two things:
How to initiate an operation
How to complete an operation
Completion tokens are further subdivided by the type of completion signature they expect. For example, WriteToken, ReadToken, HandshakeToken.
In the simplest case, a
CompletionToken
is simply a callback function that accepts arguments for information about the completed operation. When such a callback is used as a token, the operation is initiated immediately and the function is called when it completes.CompletionToken
s can also customize the initiation and return value of the initiating function in order to change the way the operation is initiated. Asio ships with several completion tokens that tweak the way a function is invoked. For example,asio::deferred
is a completion token that causes the operation to return an initiator function, that will only start the operation when it is called.amongoc
defines one customCompletionToken
of interest:amongoc::asio_as_nanosender
-
auto asio::deferred#
A
CompletionToken
that defers the initiation of an associated operation. Refer to the Asio documentation.
-
template<typename T>
concept asio::AsyncReadStream# -
CompletionToken auto token#
-
amongoc::wire::mutable_buffer_sequence auto bufs#
-
T &stream#
Requires
Must read at least one byte of data from the underlying stream into the buffers
bufs
. When finished, must complete with void(asio::error_code, std::size_t).Note
When we define
AsyncReadStream
types inamongoc
, we assume thattoken
is a regular completion handler: It does not use the fullCompletionToken
interface.
-
CompletionToken auto token#
-
template<typename T>
concept asio::AsyncWriteStream# -
CompletionToken auto token#
-
amongoc::wire::const_buffer_sequence auto bufs#
-
T &stream#
Requires
Must write at least one byte of data into the underlying stream from the buffers
bufs
, and complete with void(asio::error_code, std::size_t).
-
CompletionToken auto token#
amongoc
APIs for Asio#
-
auto amongoc::asio_as_nanosender#
This object implements the
asio::CompletionToken
interface and causes any Asio asynchronous operation APIs to return ananosender
instead of initiating the operation. This allows any Asio API to be used with theamongoc
nanosender
s transparently. Thesends_t
of the resulting nanosender is based on the completion signature of the associated operation via the following table:Asio completion signature
Resulting
sends_t
void()
mlib::unit
void(asio::error_code)
void(asio::error_code, T)
(for some type \(T\))Asio supports operations that have more than one completion signature, but
asio_as_nanosender
does not currently support this, and we do not use any Asio operations that do this.When
asio_as_nanosender
is used as anasio::CompletionToken
for an operation, the return value from the Asio API will be an unspecified type that implementsnanosender
.
-
template<typename T>
concept amongoc::wire::const_buffer_sequence# -
template<typename T>
concept amongoc::wire::mutable_buffer_sequence# These concepts correspond to Asio’s ConstBufferSequence and MutableBufferSequence concepts.
-
class amongoc::tcp_connection_rw_stream#
This is a move-only class type that partially implements
asio::AsyncReadStream
andasio::AsyncWriteStream
.-
amongoc_loop *loop#
-
unique_box conn#
loop
is the event loop associated with the connectionconn
.conn
is an opaque box that was obtained usingamongoc_loop_vtable::tcp_connect()
.
-
void async_read_some(wire::mutable_buffer_sequence auto &&bufs, auto &&cb)#
-
void async_write_some(wire::const_buffer_sequence auto &&bufs, auto &&cb)#
Implement the low-level read/write operations that Asio expects in order to construct high-level I/O algorithms.
The
cb
parameter must be a regular completion callback. The fullasio::CompletionToken
interface is not implemented.Note
Don’t use these directly. Instead, use the Asio algorithms, which will allow you to use a proper
asio::CompletionToken
and have stronger behavioral guarantees.These member functions will invoke
amongoc_loop_vtable::tcp_read_some()
andamongoc_loop_vtable::tcp_write_some()
, respectively.
-
amongoc_loop *loop#
Footnotes