Skip to content

Commit

Permalink
Clean up coro overview.
Browse files Browse the repository at this point in the history
  • Loading branch information
chriskohlhoff committed Nov 15, 2021
1 parent 9f33d21 commit 1e48957
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 63 deletions.
78 changes: 70 additions & 8 deletions asio/src/doc/overview/coro.qbk
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@

[section:coro Resumable C++ 20 Coroutines]

[note This is an experimental feature.]

The [link asio.reference.experimental__coro `experimental::coro`] class provides
support for a universal C++20 coroutine. The can be used as tasks, generators
and transfomers, depending on their signature.
support for a universal C++20 coroutine. These coroutines can be used as tasks,
generators and transfomers, depending on their signature.

coro<std::string_view> line_reader(tcp::socket stream)
{
Expand Down Expand Up @@ -58,7 +60,7 @@ that it can cover a set of different use cases.
typename Executor = any_io_executor>
struct coro;

[h3 Yield]
[heading Yield]

The `Yield` parameter designates how a `co_yield` statement behaves. It can
either be a type, like `int` or a signature with zero or one types:
Expand Down Expand Up @@ -103,7 +105,7 @@ ignored because the coroutines are lazy.
assert(25 == co_await sum.async_resume(0, use_awaitable));
}

[h3 `noexcept`]
[heading `noexcept`]

A coro may be noexcept:

Expand All @@ -118,7 +120,7 @@ ends with an exception will cause `std::terminate` to be called.
Furthermore, calls of `async_resume` and `co_await` of an expired noexcept coro
will cause undefined behaviour.

[h3 Return]
[heading Return]

A coro can also define a type that can be used with `co_return`:

Expand All @@ -129,7 +131,7 @@ A coro can also define a type that can be used with `co_return`:

A coro can have both a `Yield` and `Return` that are non void at the same time.

[h3 Result]
[heading Result]

The result type of a coroutine is dermined by both `Yield` and `Return`. Note
that in the follwing table only the yield output value is considered, i.e.
Expand All @@ -147,7 +149,7 @@ that in the follwing table only the yield output value is considered, i.e.
[[`void`] [`T`] [`true`] [`optional<T>`] [`void(T)`]]
]

[h3 Executor]
[heading Executor]

Every coroutine needs to have its own executor. Since the coroutine gets called
multiple times, it cannot take the executor from the caller like an
Expand Down Expand Up @@ -185,7 +187,7 @@ context, to override the one of the object:
coro<int> my_coro(any_io_executor exec); // it will use exec
};

[h3 `co_await`]
[heading `co_await`]

The @c co_await within a `coro` is not the same as `async_resume(use_coro)`,
unless both coros use different executors. If they use the same, the `coro`
Expand All @@ -195,4 +197,64 @@ executor.
`co_await this_coro::` behaves the same as coroutines that use
@c asio::awaitable.

[heading Integrating with awaitable]

As the `coro` member function `async_resume` is an asynchronous operation, it
may also be used in conjunction with `awaitable` coroutines in a single control
flow. For example:

#include <asio.hpp>
#include <asio/experimental/coro.hpp>

using asio::ip::tcp;

asio::experimental::coro<std::string> reader(tcp::socket& sock)
{
std::string buf;
while (sock.is_open())
{
std::size_t n = co_await asio::async_read_until(
sock, asio::dynamic_buffer(buf), '\n',
asio::experimental::use_coro);
co_yield buf.substr(0, n);
buf.erase(0, n);
}
}

asio::awaitable<void> consumer(tcp::socket sock)
{
auto r = reader(sock);
auto msg1 = co_await r.async_resume(asio::use_awaitable);
std::cout << "Message 1: " << msg1.value_or("\n");
auto msg2 = co_await r.async_resume(asio::use_awaitable);
std::cout << "Message 2: " << msg2.value_or("\n");
}

asio::awaitable<void> listen(tcp::acceptor& acceptor)
{
for (;;)
{
co_spawn(
acceptor.get_executor(),
consumer(co_await acceptor.async_accept(asio::use_awaitable)),
asio::detached);
}
}

int main()
{
asio::io_context ctx;
tcp::acceptor acceptor(ctx, {tcp::v4(), 54321});
co_spawn(ctx, listen(acceptor), asio::detached);
ctx.run();
}

[heading See Also]

[link asio.reference.co_spawn co_spawn],
[link asio.reference.experimental__coro experimental::coro],
[link asio.overview.core.cpp20_coroutines C++20 Coroutines],
[link asio.overview.core.spawn Stackful Coroutines],
[link asio.overview.core.coroutine Stackless Coroutines].

[endsect]
56 changes: 1 addition & 55 deletions asio/src/doc/overview/cpp20_coroutines.qbk
Original file line number Diff line number Diff line change
Expand Up @@ -155,61 +155,6 @@ namespace into scope:

using namespace asio::experimental::awaitable_operators;

[heading Coroutines that Await and Yield]

[note This is an experimental feature.]

The [link asio.reference.experimental__coro `coro`] type is a C++20 coroutine
primitive for resumable functions, with the ability to combine both
asynchronous waiting (`co_await`) and yielding (`co_yield`) into a single,
stateful control flow. For example:

#include <asio.hpp>
#include <asio/experimental/coro.hpp>

using asio::ip::tcp;

asio::experimental::coro<std::string> reader(tcp::socket& sock)
{
std::string buf;
while (sock.is_open())
{
std::size_t n = co_await asio::async_read_until(
sock, asio::dynamic_buffer(buf), '\n',
asio::experimental::use_coro);
co_yield buf.substr(0, n);
buf.erase(0, n);
}
}

asio::awaitable<void> consumer(tcp::socket sock)
{
auto r = reader(sock);
auto msg1 = co_await r.async_resume(asio::use_awaitable);
std::cout << "Message 1: " << msg1.value_or("\n");
auto msg2 = co_await r.async_resume(asio::use_awaitable);
std::cout << "Message 2: " << msg2.value_or("\n");
}

asio::awaitable<void> listen(tcp::acceptor& acceptor)
{
for (;;)
{
co_spawn(
acceptor.get_executor(),
consumer(co_await acceptor.async_accept(asio::use_awaitable)),
asio::detached);
}
}

int main()
{
asio::io_context ctx;
tcp::acceptor acceptor(ctx, {tcp::v4(), 54321});
co_spawn(ctx, listen(acceptor), asio::detached);
ctx.run();
}

[heading See Also]

[link asio.reference.co_spawn co_spawn],
Expand All @@ -221,6 +166,7 @@ stateful control flow. For example:
[link asio.reference.this_coro__executor this_coro::executor],
[link asio.reference.experimental__coro experimental::coro],
[link asio.examples.cpp17_examples.coroutines_ts_support Coroutines examples],
[link asio.overview.core.coro Resumable C++20 Coroutines],
[link asio.overview.core.spawn Stackful Coroutines],
[link asio.overview.core.coroutine Stackless Coroutines].

Expand Down

0 comments on commit 1e48957

Please sign in to comment.