This means that use_coro does not return a coro object, just like
use_awaitable does, i.e. it's an overhead that buys us type erasure.
Allocators can now be set for coro by including allocator_arg in the
coro signature.
This is no longer an experimental facility. The names deferred and
deferred_t have been temporarily retained as deprecated entities under
the asio::experimental namespace, for backwards compatibility.
This is no longer an experimental facility. The names prepend and
prepend_t have been temporarily retained as deprecated entities under
the asio::experimental namespace, for backwards compatibility.
This is no longer an experimental facility. The names append and
append_t have been temporarily retained as deprecated entities under
the asio::experimental namespace, for backwards compatibility.
This is no longer an experimental facility. The names as_tuple and
as_tuple_t have been temporarily retained as deprecated entities under
the asio::experimental namespace, for backwards compatibility.
This adds experimental::channel and experimental::concurrent_channel.
Channels may be used to send completions as messages. For example:
// Create a channel with no buffer space.
channel<void(error_code, size_t)> ch(ctx);
// The call to try_send fails as there is no buffer
// space and no waiting receive operations.
bool ok = ch.try_send(asio::error::eof, 123);
assert(!ok);
// The async_send operation blocks until a receive
// operation consumes the message.
ch.async_send(asio::error::eof, 123,
[](error_code ec)
{
// ...
});
// The async_receive consumes the message. Both the
// async_send and async_receive operations complete
// immediately.
ch.async_receive(
[](error_code ec, size_t n)
{
// ...
});
* Added overload so member functions can provide an explicit executor.
* Added co_spawn for coro tasks.
* Added reference and overview documentation.
* Adopted awaitable cancellation model.
* Refactored implementation.
By default, awaitable<>-based coroutines now throw an exception if they
have been previously cancelled, and then try to perform a co_await
against another awaitable<>.
To disable this behaviour for the current awaitable<>-based "thread",
perform:
co_await boost::asio::this_coro::throw_if_error(false);
It is then the responsibility of the coroutine implementation to ensure
that it checks the cancellation state of the coroutine manually, by
doing something like:
auto cs = boost::asio::this_coro::cancellation_state;
// ...
if (cs.cancelled() != cancellation_type::none)
{
// ... handle cancellation ...
}
The logical operators || and && have been overloaded for awaitable<>, to
allow coroutines to be trivially awaited in parallel.
When awaited using &&, the await expression waits until both operations
have completed successfully. As a "short-circuit" evaluation, if one
operation fails with an exception, the other is immediately cancelled.
For example:
std::tuple<std::size_t, std::size_t> results =
co_await (
async_read(socket, input_buffer, use_awaitable)
&& async_write(socket, output_buffer, use_awaitable)
);
When awaited using ||, the await expression waits until either operation
succceeds. As a "short-circuit" evaluation, if one operation succeeds
without throwing an exception, the other is immediately cancelled. For
example:
std::variant<std::size_t, std::monostate> results =
co_await (
async_read(socket, input_buffer, use_awaitable)
|| timer.async_wait(use_awaitable)
);
The operators may be enabled by adding the #include:
#include <asio/experimental/awaitable_operators.hpp>
and then bringing the contents of the experimental::awaitable_operators
namespace into scope:
using namespace boost::asio::experimental::awaitable_operators;
The experimental::promise type allows eager execution and
synchronisation of async operations.
auto promise = async_read(
stream, asio::buffer(my_buffer),
asio::experimental::use_promise);
... do other stuff while the read is going on ...
promise.async_wait( // completion the operation
[](error_code ec, std::size_t bytes_read)
{
...
});
Promises can be safely disregarded if the result is no longer required.
Different operations can be combined to either wait for all to complete
or for one to complete (and cancel the rest). For example, to wait for
one to complete:
auto timeout_promise =
timer.async_wait(
asio::experimental::use_promise);
auto read_promise = async_read(
stream, asio::buffer(my_buffer),
asio::experimental::use_promise);
auto promise =
asio::experimental::promise<>::race(
timeout_promise, read_promise);
promise.async_wait(
[](std::variant<error_code, std::tuple<error_code, std::size_t>> v)
{
if (v.index() == 0) {} //timed out
else if (v.index() == 1) // completed in time
});
or to wait for all to complete:
auto write_promise = async_write(
stream, asio::buffer(my_write_buffer),
asio::experimental::use_promise);
auto read_promise = async_read(
stream, asio::buffer(my_buffer),
asio::experimental::use_promise);
auto promise =
asio::experimental::promise<>::all(
write_promise, read_promise);
promise.async_wait(
[](std::tuple<error_code, std::size_t> write_result,
std::tuple<error_code, std::size_t> read_result)
{
});