diff options
Diffstat (limited to '3rdparty/asio/src/examples/cpp20/coroutines')
9 files changed, 805 insertions, 0 deletions
diff --git a/3rdparty/asio/src/examples/cpp20/coroutines/chat_server.cpp b/3rdparty/asio/src/examples/cpp20/coroutines/chat_server.cpp new file mode 100644 index 00000000000..cd5fba32486 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp20/coroutines/chat_server.cpp @@ -0,0 +1,222 @@ +// +// chat_server.cpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include <cstdlib> +#include <deque> +#include <iostream> +#include <list> +#include <memory> +#include <set> +#include <string> +#include <utility> +#include <asio/awaitable.hpp> +#include <asio/detached.hpp> +#include <asio/co_spawn.hpp> +#include <asio/io_context.hpp> +#include <asio/ip/tcp.hpp> +#include <asio/read_until.hpp> +#include <asio/redirect_error.hpp> +#include <asio/signal_set.hpp> +#include <asio/steady_timer.hpp> +#include <asio/use_awaitable.hpp> +#include <asio/write.hpp> + +using asio::ip::tcp; +using asio::awaitable; +using asio::co_spawn; +using asio::detached; +using asio::redirect_error; +using asio::use_awaitable; + +//---------------------------------------------------------------------- + +class chat_participant +{ +public: + virtual ~chat_participant() {} + virtual void deliver(const std::string& msg) = 0; +}; + +typedef std::shared_ptr<chat_participant> chat_participant_ptr; + +//---------------------------------------------------------------------- + +class chat_room +{ +public: + void join(chat_participant_ptr participant) + { + participants_.insert(participant); + for (auto msg: recent_msgs_) + participant->deliver(msg); + } + + void leave(chat_participant_ptr participant) + { + participants_.erase(participant); + } + + void deliver(const std::string& msg) + { + recent_msgs_.push_back(msg); + while (recent_msgs_.size() > max_recent_msgs) + recent_msgs_.pop_front(); + + for (auto participant: participants_) + participant->deliver(msg); + } + +private: + std::set<chat_participant_ptr> participants_; + enum { max_recent_msgs = 100 }; + std::deque<std::string> recent_msgs_; +}; + +//---------------------------------------------------------------------- + +class chat_session + : public chat_participant, + public std::enable_shared_from_this<chat_session> +{ +public: + chat_session(tcp::socket socket, chat_room& room) + : socket_(std::move(socket)), + timer_(socket_.get_executor()), + room_(room) + { + timer_.expires_at(std::chrono::steady_clock::time_point::max()); + } + + void start() + { + room_.join(shared_from_this()); + + co_spawn(socket_.get_executor(), + [self = shared_from_this()]{ return self->reader(); }, + detached); + + co_spawn(socket_.get_executor(), + [self = shared_from_this()]{ return self->writer(); }, + detached); + } + + void deliver(const std::string& msg) + { + write_msgs_.push_back(msg); + timer_.cancel_one(); + } + +private: + awaitable<void> reader() + { + try + { + for (std::string read_msg;;) + { + std::size_t n = co_await asio::async_read_until(socket_, + asio::dynamic_buffer(read_msg, 1024), "\n", use_awaitable); + + room_.deliver(read_msg.substr(0, n)); + read_msg.erase(0, n); + } + } + catch (std::exception&) + { + stop(); + } + } + + awaitable<void> writer() + { + try + { + while (socket_.is_open()) + { + if (write_msgs_.empty()) + { + asio::error_code ec; + co_await timer_.async_wait(redirect_error(use_awaitable, ec)); + } + else + { + co_await asio::async_write(socket_, + asio::buffer(write_msgs_.front()), use_awaitable); + write_msgs_.pop_front(); + } + } + } + catch (std::exception&) + { + stop(); + } + } + + void stop() + { + room_.leave(shared_from_this()); + socket_.close(); + timer_.cancel(); + } + + tcp::socket socket_; + asio::steady_timer timer_; + chat_room& room_; + std::deque<std::string> write_msgs_; +}; + +//---------------------------------------------------------------------- + +awaitable<void> listener(tcp::acceptor acceptor) +{ + chat_room room; + + for (;;) + { + std::make_shared<chat_session>( + co_await acceptor.async_accept(use_awaitable), + room + )->start(); + } +} + +//---------------------------------------------------------------------- + +int main(int argc, char* argv[]) +{ + try + { + if (argc < 2) + { + std::cerr << "Usage: chat_server <port> [<port> ...]\n"; + return 1; + } + + asio::io_context io_context(1); + + for (int i = 1; i < argc; ++i) + { + unsigned short port = std::atoi(argv[i]); + co_spawn(io_context, + listener(tcp::acceptor(io_context, {tcp::v4(), port})), + detached); + } + + asio::signal_set signals(io_context, SIGINT, SIGTERM); + signals.async_wait([&](auto, auto){ io_context.stop(); }); + + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/3rdparty/asio/src/examples/cpp20/coroutines/echo_server.cpp b/3rdparty/asio/src/examples/cpp20/coroutines/echo_server.cpp new file mode 100644 index 00000000000..a9532459f77 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp20/coroutines/echo_server.cpp @@ -0,0 +1,76 @@ +// +// echo_server.cpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include <asio/co_spawn.hpp> +#include <asio/detached.hpp> +#include <asio/io_context.hpp> +#include <asio/ip/tcp.hpp> +#include <asio/signal_set.hpp> +#include <asio/write.hpp> +#include <cstdio> + +using asio::ip::tcp; +using asio::awaitable; +using asio::co_spawn; +using asio::detached; +using asio::use_awaitable; +namespace this_coro = asio::this_coro; + +#if defined(ASIO_ENABLE_HANDLER_TRACKING) +# define use_awaitable \ + asio::use_awaitable_t(__FILE__, __LINE__, __PRETTY_FUNCTION__) +#endif + +awaitable<void> echo(tcp::socket socket) +{ + try + { + char data[1024]; + for (;;) + { + std::size_t n = co_await socket.async_read_some(asio::buffer(data), use_awaitable); + co_await async_write(socket, asio::buffer(data, n), use_awaitable); + } + } + catch (std::exception& e) + { + std::printf("echo Exception: %s\n", e.what()); + } +} + +awaitable<void> listener() +{ + auto executor = co_await this_coro::executor; + tcp::acceptor acceptor(executor, {tcp::v4(), 55555}); + for (;;) + { + tcp::socket socket = co_await acceptor.async_accept(use_awaitable); + co_spawn(executor, echo(std::move(socket)), detached); + } +} + +int main() +{ + try + { + asio::io_context io_context(1); + + asio::signal_set signals(io_context, SIGINT, SIGTERM); + signals.async_wait([&](auto, auto){ io_context.stop(); }); + + co_spawn(io_context, listener(), detached); + + io_context.run(); + } + catch (std::exception& e) + { + std::printf("Exception: %s\n", e.what()); + } +} diff --git a/3rdparty/asio/src/examples/cpp20/coroutines/echo_server_with_as_single_default.cpp b/3rdparty/asio/src/examples/cpp20/coroutines/echo_server_with_as_single_default.cpp new file mode 100644 index 00000000000..4a692acc872 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp20/coroutines/echo_server_with_as_single_default.cpp @@ -0,0 +1,71 @@ +// +// echo_server_with_as_single_default.cpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include <asio/experimental/as_single.hpp> +#include <asio/co_spawn.hpp> +#include <asio/detached.hpp> +#include <asio/io_context.hpp> +#include <asio/ip/tcp.hpp> +#include <asio/signal_set.hpp> +#include <asio/write.hpp> +#include <cstdio> + +using asio::experimental::as_single_t; +using asio::ip::tcp; +using asio::awaitable; +using asio::co_spawn; +using asio::detached; +using asio::use_awaitable_t; +using default_token = as_single_t<use_awaitable_t<>>; +using tcp_acceptor = default_token::as_default_on_t<tcp::acceptor>; +using tcp_socket = default_token::as_default_on_t<tcp::socket>; +namespace this_coro = asio::this_coro; + +awaitable<void> echo(tcp_socket socket) +{ + char data[1024]; + for (;;) + { + auto [e1, nread] = co_await socket.async_read_some(asio::buffer(data)); + if (nread == 0) break; + auto [e2, nwritten] = co_await async_write(socket, asio::buffer(data, nread)); + if (nwritten != nread) break; + } +} + +awaitable<void> listener() +{ + auto executor = co_await this_coro::executor; + tcp_acceptor acceptor(executor, {tcp::v4(), 55555}); + for (;;) + { + if (auto [e, socket] = co_await acceptor.async_accept(); socket.is_open()) + co_spawn(executor, echo(std::move(socket)), detached); + } +} + +int main() +{ + try + { + asio::io_context io_context(1); + + asio::signal_set signals(io_context, SIGINT, SIGTERM); + signals.async_wait([&](auto, auto){ io_context.stop(); }); + + co_spawn(io_context, listener(), detached); + + io_context.run(); + } + catch (std::exception& e) + { + std::printf("Exception: %s\n", e.what()); + } +} diff --git a/3rdparty/asio/src/examples/cpp20/coroutines/echo_server_with_as_tuple_default.cpp b/3rdparty/asio/src/examples/cpp20/coroutines/echo_server_with_as_tuple_default.cpp new file mode 100644 index 00000000000..de162cad78e --- /dev/null +++ b/3rdparty/asio/src/examples/cpp20/coroutines/echo_server_with_as_tuple_default.cpp @@ -0,0 +1,71 @@ +// +// echo_server_with_as_tuple_default.cpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include <asio/as_tuple.hpp> +#include <asio/co_spawn.hpp> +#include <asio/detached.hpp> +#include <asio/io_context.hpp> +#include <asio/ip/tcp.hpp> +#include <asio/signal_set.hpp> +#include <asio/write.hpp> +#include <cstdio> + +using asio::as_tuple_t; +using asio::ip::tcp; +using asio::awaitable; +using asio::co_spawn; +using asio::detached; +using asio::use_awaitable_t; +using default_token = as_tuple_t<use_awaitable_t<>>; +using tcp_acceptor = default_token::as_default_on_t<tcp::acceptor>; +using tcp_socket = default_token::as_default_on_t<tcp::socket>; +namespace this_coro = asio::this_coro; + +awaitable<void> echo(tcp_socket socket) +{ + char data[1024]; + for (;;) + { + auto [e1, nread] = co_await socket.async_read_some(asio::buffer(data)); + if (nread == 0) break; + auto [e2, nwritten] = co_await async_write(socket, asio::buffer(data, nread)); + if (nwritten != nread) break; + } +} + +awaitable<void> listener() +{ + auto executor = co_await this_coro::executor; + tcp_acceptor acceptor(executor, {tcp::v4(), 55555}); + for (;;) + { + if (auto [e, socket] = co_await acceptor.async_accept(); socket.is_open()) + co_spawn(executor, echo(std::move(socket)), detached); + } +} + +int main() +{ + try + { + asio::io_context io_context(1); + + asio::signal_set signals(io_context, SIGINT, SIGTERM); + signals.async_wait([&](auto, auto){ io_context.stop(); }); + + co_spawn(io_context, listener(), detached); + + io_context.run(); + } + catch (std::exception& e) + { + std::printf("Exception: %s\n", e.what()); + } +} diff --git a/3rdparty/asio/src/examples/cpp20/coroutines/echo_server_with_default.cpp b/3rdparty/asio/src/examples/cpp20/coroutines/echo_server_with_default.cpp new file mode 100644 index 00000000000..54344d94d56 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp20/coroutines/echo_server_with_default.cpp @@ -0,0 +1,73 @@ +// +// echo_server_with_default.cpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include <asio/co_spawn.hpp> +#include <asio/detached.hpp> +#include <asio/io_context.hpp> +#include <asio/ip/tcp.hpp> +#include <asio/signal_set.hpp> +#include <asio/write.hpp> +#include <cstdio> + +using asio::ip::tcp; +using asio::awaitable; +using asio::co_spawn; +using asio::detached; +using asio::use_awaitable_t; +using tcp_acceptor = use_awaitable_t<>::as_default_on_t<tcp::acceptor>; +using tcp_socket = use_awaitable_t<>::as_default_on_t<tcp::socket>; +namespace this_coro = asio::this_coro; + +awaitable<void> echo(tcp_socket socket) +{ + try + { + char data[1024]; + for (;;) + { + std::size_t n = co_await socket.async_read_some(asio::buffer(data)); + co_await async_write(socket, asio::buffer(data, n)); + } + } + catch (std::exception& e) + { + std::printf("echo Exception: %s\n", e.what()); + } +} + +awaitable<void> listener() +{ + auto executor = co_await this_coro::executor; + tcp_acceptor acceptor(executor, {tcp::v4(), 55555}); + for (;;) + { + auto socket = co_await acceptor.async_accept(); + co_spawn(executor, echo(std::move(socket)), detached); + } +} + +int main() +{ + try + { + asio::io_context io_context(1); + + asio::signal_set signals(io_context, SIGINT, SIGTERM); + signals.async_wait([&](auto, auto){ io_context.stop(); }); + + co_spawn(io_context, listener(), detached); + + io_context.run(); + } + catch (std::exception& e) + { + std::printf("Exception: %s\n", e.what()); + } +} diff --git a/3rdparty/asio/src/examples/cpp20/coroutines/echo_server_with_deferred.cpp b/3rdparty/asio/src/examples/cpp20/coroutines/echo_server_with_deferred.cpp new file mode 100644 index 00000000000..54469ba7aba --- /dev/null +++ b/3rdparty/asio/src/examples/cpp20/coroutines/echo_server_with_deferred.cpp @@ -0,0 +1,72 @@ +// +// echo_server.cpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include <asio/co_spawn.hpp> +#include <asio/deferred.hpp> +#include <asio/detached.hpp> +#include <asio/io_context.hpp> +#include <asio/ip/tcp.hpp> +#include <asio/signal_set.hpp> +#include <asio/write.hpp> +#include <cstdio> + +using asio::ip::tcp; +using asio::awaitable; +using asio::co_spawn; +using asio::deferred; +using asio::detached; +namespace this_coro = asio::this_coro; + +awaitable<void> echo(tcp::socket socket) +{ + try + { + char data[1024]; + for (;;) + { + std::size_t n = co_await socket.async_read_some(asio::buffer(data), deferred); + co_await async_write(socket, asio::buffer(data, n), deferred); + } + } + catch (std::exception& e) + { + std::printf("echo Exception: %s\n", e.what()); + } +} + +awaitable<void> listener() +{ + auto executor = co_await this_coro::executor; + tcp::acceptor acceptor(executor, {tcp::v4(), 55555}); + for (;;) + { + tcp::socket socket = co_await acceptor.async_accept(deferred); + co_spawn(executor, echo(std::move(socket)), detached); + } +} + +int main() +{ + try + { + asio::io_context io_context(1); + + asio::signal_set signals(io_context, SIGINT, SIGTERM); + signals.async_wait([&](auto, auto){ io_context.stop(); }); + + co_spawn(io_context, listener(), detached); + + io_context.run(); + } + catch (std::exception& e) + { + std::printf("Exception: %s\n", e.what()); + } +} diff --git a/3rdparty/asio/src/examples/cpp20/coroutines/echo_server_with_deferred_default.cpp b/3rdparty/asio/src/examples/cpp20/coroutines/echo_server_with_deferred_default.cpp new file mode 100644 index 00000000000..33c383311d2 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp20/coroutines/echo_server_with_deferred_default.cpp @@ -0,0 +1,74 @@ +// +// echo_server.cpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include <asio/co_spawn.hpp> +#include <asio/deferred.hpp> +#include <asio/detached.hpp> +#include <asio/io_context.hpp> +#include <asio/ip/tcp.hpp> +#include <asio/signal_set.hpp> +#include <asio/write.hpp> +#include <cstdio> + +using asio::ip::tcp; +using asio::awaitable; +using asio::co_spawn; +using asio::detached; +using default_token = asio::deferred_t; +using tcp_acceptor = default_token::as_default_on_t<tcp::acceptor>; +using tcp_socket = default_token::as_default_on_t<tcp::socket>; +namespace this_coro = asio::this_coro; + +awaitable<void> echo(tcp_socket socket) +{ + try + { + char data[1024]; + for (;;) + { + std::size_t n = co_await socket.async_read_some(asio::buffer(data)); + co_await async_write(socket, asio::buffer(data, n)); + } + } + catch (std::exception& e) + { + std::printf("echo Exception: %s\n", e.what()); + } +} + +awaitable<void> listener() +{ + auto executor = co_await this_coro::executor; + tcp_acceptor acceptor(executor, {tcp::v4(), 55555}); + for (;;) + { + tcp::socket socket = co_await acceptor.async_accept(); + co_spawn(executor, echo(std::move(socket)), detached); + } +} + +int main() +{ + try + { + asio::io_context io_context(1); + + asio::signal_set signals(io_context, SIGINT, SIGTERM); + signals.async_wait([&](auto, auto){ io_context.stop(); }); + + co_spawn(io_context, listener(), detached); + + io_context.run(); + } + catch (std::exception& e) + { + std::printf("Exception: %s\n", e.what()); + } +} diff --git a/3rdparty/asio/src/examples/cpp20/coroutines/refactored_echo_server.cpp b/3rdparty/asio/src/examples/cpp20/coroutines/refactored_echo_server.cpp new file mode 100644 index 00000000000..dc7b03d86e2 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp20/coroutines/refactored_echo_server.cpp @@ -0,0 +1,80 @@ +// +// refactored_echo_server.cpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include <asio/co_spawn.hpp> +#include <asio/detached.hpp> +#include <asio/io_context.hpp> +#include <asio/ip/tcp.hpp> +#include <asio/signal_set.hpp> +#include <asio/write.hpp> +#include <cstdio> + +using asio::ip::tcp; +using asio::awaitable; +using asio::co_spawn; +using asio::detached; +using asio::use_awaitable; +namespace this_coro = asio::this_coro; + +awaitable<void> echo_once(tcp::socket& socket) +{ + char data[128]; + std::size_t n = co_await socket.async_read_some(asio::buffer(data), use_awaitable); + co_await async_write(socket, asio::buffer(data, n), use_awaitable); +} + +awaitable<void> echo(tcp::socket socket) +{ + try + { + for (;;) + { + // The asynchronous operations to echo a single chunk of data have been + // refactored into a separate function. When this function is called, the + // operations are still performed in the context of the current + // coroutine, and the behaviour is functionally equivalent. + co_await echo_once(socket); + } + } + catch (std::exception& e) + { + std::printf("echo Exception: %s\n", e.what()); + } +} + +awaitable<void> listener() +{ + auto executor = co_await this_coro::executor; + tcp::acceptor acceptor(executor, {tcp::v4(), 55555}); + for (;;) + { + tcp::socket socket = co_await acceptor.async_accept(use_awaitable); + co_spawn(executor, echo(std::move(socket)), detached); + } +} + +int main() +{ + try + { + asio::io_context io_context(1); + + asio::signal_set signals(io_context, SIGINT, SIGTERM); + signals.async_wait([&](auto, auto){ io_context.stop(); }); + + co_spawn(io_context, listener(), detached); + + io_context.run(); + } + catch (std::exception& e) + { + std::printf("Exception: %s\n", e.what()); + } +} diff --git a/3rdparty/asio/src/examples/cpp20/coroutines/timeout.cpp b/3rdparty/asio/src/examples/cpp20/coroutines/timeout.cpp new file mode 100644 index 00000000000..2ffcab7ad35 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp20/coroutines/timeout.cpp @@ -0,0 +1,66 @@ +// +// timeout.cpp +// ~~~~~~~~~~~ +// +// Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include <asio.hpp> +#include <asio/experimental/awaitable_operators.hpp> + +using namespace asio; +using namespace asio::experimental::awaitable_operators; +using time_point = std::chrono::steady_clock::time_point; +using ip::tcp; + +awaitable<void> echo(tcp::socket& sock, time_point& deadline) +{ + char data[4196]; + for (;;) + { + deadline = std::chrono::steady_clock::now() + std::chrono::seconds(10); + auto n = co_await sock.async_read_some(buffer(data), use_awaitable); + co_await async_write(sock, buffer(data, n), use_awaitable); + } +} + +awaitable<void> watchdog(time_point& deadline) +{ + steady_timer timer(co_await this_coro::executor); + auto now = std::chrono::steady_clock::now(); + while (deadline > now) + { + timer.expires_at(deadline); + co_await timer.async_wait(use_awaitable); + now = std::chrono::steady_clock::now(); + } + throw std::system_error(std::make_error_code(std::errc::timed_out)); +} + +awaitable<void> handle_connection(tcp::socket sock) +{ + time_point deadline{}; + co_await (echo(sock, deadline) && watchdog(deadline)); +} + +awaitable<void> listen(tcp::acceptor& acceptor) +{ + for (;;) + { + co_spawn( + acceptor.get_executor(), + handle_connection(co_await acceptor.async_accept(use_awaitable)), + detached); + } +} + +int main() +{ + io_context ctx; + tcp::acceptor acceptor(ctx, {tcp::v4(), 54321}); + co_spawn(ctx, listen(acceptor), detached); + ctx.run(); +} |