2
0
mirror of https://github.com/boostorg/asio.git synced 2026-01-19 16:12:09 +00:00
Files
asio/test/co_composed.cpp
Christopher Kohlhoff 1afbc5c12b Update copyright notices.
2025-03-04 22:57:26 +11:00

566 lines
15 KiB
C++

//
// co_composed.cpp
// ~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2025 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)
//
// Disable autolinking for unit tests.
#if !defined(BOOST_ALL_NO_LIB)
#define BOOST_ALL_NO_LIB 1
#endif // !defined(BOOST_ALL_NO_LIB)
// Prevent link dependency on the Boost.System library.
#if !defined(BOOST_SYSTEM_NO_DEPRECATED)
#define BOOST_SYSTEM_NO_DEPRECATED
#endif // !defined(BOOST_SYSTEM_NO_DEPRECATED)
// Test that header file is self-contained.
#include <boost/asio/co_composed.hpp>
#include "unit_test.hpp"
#if defined(BOOST_ASIO_HAS_CO_AWAIT)
#include <boost/asio/bind_cancellation_slot.hpp>
#include <boost/asio/deferred.hpp>
#include <boost/asio/detached.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/post.hpp>
template <typename CompletionToken>
auto async_throw(CompletionToken&& token)
{
return boost::asio::async_initiate<CompletionToken, void()>(
[](auto) { throw 42; }, token);
}
template <typename CompletionToken>
auto throw_first(CompletionToken&& token)
{
return boost::asio::async_initiate<CompletionToken, void()>(
boost::asio::co_composed(
[](auto state) -> void
{
throw 42;
co_yield state.complete();
}), token);
}
void test_throw_first()
{
try
{
throw_first(boost::asio::detached);
BOOST_ASIO_CHECK(0);
}
catch (int)
{
}
}
template <typename CompletionToken>
auto throw_after_await(boost::asio::io_context& ctx, CompletionToken&& token)
{
return boost::asio::async_initiate<CompletionToken, void()>(
boost::asio::co_composed(
[](auto state, boost::asio::io_context& ctx) -> void
{
co_await boost::asio::post(ctx, boost::asio::deferred);
throw 42;
co_yield state.complete();
}), token, std::ref(ctx));
}
void test_throw_after_await()
{
try
{
boost::asio::io_context ctx(1);
throw_after_await(ctx, boost::asio::detached);
ctx.run();
BOOST_ASIO_CHECK(0);
}
catch (int)
{
}
}
template <typename CompletionToken>
auto throw_in_first_suspend(CompletionToken&& token)
{
return boost::asio::async_initiate<CompletionToken, void()>(
boost::asio::co_composed(
[](auto state) -> void
{
co_await async_throw(boost::asio::deferred);
co_yield state.complete();
}), token);
}
void test_throw_in_first_suspend()
{
try
{
throw_in_first_suspend(boost::asio::detached);
BOOST_ASIO_CHECK(0);
}
catch (int)
{
}
}
template <typename CompletionToken>
auto throw_in_suspend_after_await(
boost::asio::io_context& ctx, CompletionToken&& token)
{
return boost::asio::async_initiate<CompletionToken, void()>(
boost::asio::co_composed(
[](auto state, boost::asio::io_context& ctx) -> void
{
co_await boost::asio::post(ctx, boost::asio::deferred);
co_await async_throw(boost::asio::deferred);
co_yield state.complete();
}), token, std::ref(ctx));
}
void test_throw_in_suspend_after_await()
{
try
{
boost::asio::io_context ctx(1);
throw_in_suspend_after_await(ctx, boost::asio::detached);
ctx.run();
BOOST_ASIO_CHECK(0);
}
catch (int)
{
}
}
template <typename CompletionToken>
auto post_loop(boost::asio::io_context& ctx, CompletionToken&& token)
{
return boost::asio::async_initiate<CompletionToken, void(int)>(
boost::asio::co_composed(
[](auto state, boost::asio::io_context& ctx) -> void
{
int i = 0;
for (; i < 100; ++i)
co_await boost::asio::post(ctx, boost::asio::deferred);
co_yield state.complete(i);
}, ctx), token, std::ref(ctx));
}
void test_post_loop()
{
boost::asio::io_context ctx(1);
int count = 0;
post_loop(ctx, [&](int i){ count = i; });
ctx.run();
BOOST_ASIO_CHECK(count == 100);
}
template <typename CompletionToken>
auto nested_post(boost::asio::io_context& ctx, CompletionToken&& token)
{
return boost::asio::async_initiate<CompletionToken, void()>(
boost::asio::co_composed(
[](auto state, boost::asio::io_context& ctx) -> void
{
co_await boost::asio::post(ctx, boost::asio::deferred);
co_yield state.complete();
}, ctx), token, std::ref(ctx));
}
template <typename CompletionToken>
auto nested_post_loop(boost::asio::io_context& ctx, CompletionToken&& token)
{
return boost::asio::async_initiate<CompletionToken, void(int)>(
boost::asio::co_composed(
[](auto state, boost::asio::io_context& ctx) -> void
{
int i = 0;
for (; i < 100; ++i)
co_await nested_post(ctx, boost::asio::deferred);
co_yield state.complete(i);
}, ctx), token, std::ref(ctx));
}
void test_nested_post_loop()
{
boost::asio::io_context ctx(1);
int count = 0;
nested_post_loop(ctx, [&](int i){ count = i; });
ctx.run();
BOOST_ASIO_CHECK(count == 100);
}
template <typename CompletionToken>
auto post_loop_return_1_0(boost::asio::io_context& ctx, CompletionToken&& token)
{
return boost::asio::async_initiate<CompletionToken, void()>(
boost::asio::co_composed<void()>(
[](auto, boost::asio::io_context& ctx) -> void
{
int i = 0;
for (; i < 100; ++i)
co_await boost::asio::post(ctx, boost::asio::deferred);
co_return {};
}, ctx), token, std::ref(ctx));
}
void test_post_loop_return_1_0()
{
boost::asio::io_context ctx(1);
bool done = false;
post_loop_return_1_0(ctx, [&]{ done = true; });
ctx.run();
BOOST_ASIO_CHECK(done);
}
template <typename CompletionToken>
auto post_loop_return_1_1(boost::asio::io_context& ctx, CompletionToken&& token)
{
return boost::asio::async_initiate<CompletionToken, void(int)>(
boost::asio::co_composed<void(int)>(
[](auto, boost::asio::io_context& ctx) -> void
{
int i = 0;
for (; i < 100; ++i)
co_await boost::asio::post(ctx, boost::asio::deferred);
co_return {i};
}, ctx), token, std::ref(ctx));
}
void test_post_loop_return_1_1()
{
boost::asio::io_context ctx(1);
int count = 0;
post_loop_return_1_1(ctx, [&](int i){ count = i; });
ctx.run();
BOOST_ASIO_CHECK(count == 100);
}
template <typename CompletionToken>
auto post_loop_return_1_2(boost::asio::io_context& ctx, CompletionToken&& token)
{
return boost::asio::async_initiate<CompletionToken, void(int, char)>(
boost::asio::co_composed<void(int, char)>(
[](auto, boost::asio::io_context& ctx) -> void
{
int i = 0;
for (; i < 100; ++i)
co_await boost::asio::post(ctx, boost::asio::deferred);
co_return {i, 'A'};
}, ctx), token, std::ref(ctx));
}
void test_post_loop_return_1_2()
{
boost::asio::io_context ctx(1);
int count = 0;
char ch = 0;
post_loop_return_1_2(ctx, [&](int i, char c){ count = i, ch = c; });
ctx.run();
BOOST_ASIO_CHECK(count == 100);
BOOST_ASIO_CHECK(ch == 'A');
}
template <typename CompletionToken>
auto post_loop_return_2(boost::asio::io_context& ctx, CompletionToken&& token)
{
return boost::asio::async_initiate<CompletionToken, void(), void(int)>(
boost::asio::co_composed<void(), void(int)>(
[](auto, boost::asio::io_context& ctx) -> void
{
int i = 0;
for (; i < 100; ++i)
co_await boost::asio::post(ctx, boost::asio::deferred);
co_return {i};
}, ctx), token, std::ref(ctx));
}
void test_post_loop_return_2()
{
boost::asio::io_context ctx(1);
int count = 0;
post_loop_return_2(ctx, [&](int i = 0){ count = i; });
ctx.run();
BOOST_ASIO_CHECK(count == 100);
}
template <typename CompletionToken>
auto complete_on_cancel(boost::asio::io_context& ctx, CompletionToken&& token)
{
return boost::asio::async_initiate<
CompletionToken, void(boost::system::error_code, int)>(
boost::asio::co_composed<
void(boost::system::error_code, int)>(
[](auto state, boost::asio::io_context& ctx) -> void
{
state.on_cancellation_complete_with(
boost::asio::error::invalid_argument, 42);
int i = 0;
for (; i < 100; ++i)
co_await boost::asio::post(ctx, boost::asio::deferred);
co_return {boost::asio::error::eof, i};
}, ctx), token, std::ref(ctx));
}
void test_complete_on_cancel()
{
boost::asio::io_context ctx(1);
int count = 0;
boost::system::error_code ec;
boost::asio::cancellation_signal cancel;
complete_on_cancel(ctx,
[&](boost::system::error_code e, int i)
{
ec = e;
count = i;
});
ctx.run();
BOOST_ASIO_CHECK(ec == boost::asio::error::eof);
BOOST_ASIO_CHECK(count == 100);
complete_on_cancel(ctx,
boost::asio::bind_cancellation_slot(cancel.slot(),
[&](boost::system::error_code e, int i)
{
ec = e;
count = i;
}));
ctx.restart();
ctx.run_one();
cancel.emit(boost::asio::cancellation_type::all);
ctx.run();
BOOST_ASIO_CHECK(ec == boost::asio::error::invalid_argument);
BOOST_ASIO_CHECK(count == 42);
complete_on_cancel(ctx,
boost::asio::bind_cancellation_slot(cancel.slot(),
[&](boost::system::error_code e, int i)
{
ec = e;
count = i;
}));
ctx.restart();
ctx.run();
BOOST_ASIO_CHECK(ec == boost::asio::error::eof);
BOOST_ASIO_CHECK(count == 100);
}
template <typename CompletionToken>
auto complete_with_default_on_cancel(
boost::asio::io_context& ctx, CompletionToken&& token)
{
return boost::asio::async_initiate<
CompletionToken, void(boost::system::error_code, int)>(
boost::asio::co_composed<
void(boost::system::error_code, int)>(
[](auto, boost::asio::io_context& ctx) -> void
{
int i = 0;
for (; i < 100; ++i)
co_await boost::asio::post(ctx, boost::asio::deferred);
co_return {boost::asio::error::eof, i};
}, ctx), token, std::ref(ctx));
}
void test_complete_with_default_on_cancel()
{
boost::asio::io_context ctx(1);
int count = 0;
boost::system::error_code ec;
boost::asio::cancellation_signal cancel;
complete_with_default_on_cancel(ctx,
[&](boost::system::error_code e, int i)
{
ec = e;
count = i;
});
ctx.run();
BOOST_ASIO_CHECK(ec == boost::asio::error::eof);
BOOST_ASIO_CHECK(count == 100);
complete_with_default_on_cancel(ctx,
boost::asio::bind_cancellation_slot(cancel.slot(),
[&](boost::system::error_code e, int i)
{
ec = e;
count = i;
}));
ctx.restart();
ctx.run_one();
cancel.emit(boost::asio::cancellation_type::all);
ctx.run();
BOOST_ASIO_CHECK(ec == boost::asio::error::operation_aborted);
BOOST_ASIO_CHECK(count == 0);
complete_with_default_on_cancel(ctx,
boost::asio::bind_cancellation_slot(cancel.slot(),
[&](boost::system::error_code e, int i)
{
ec = e;
count = i;
}));
ctx.restart();
ctx.run();
BOOST_ASIO_CHECK(ec == boost::asio::error::eof);
BOOST_ASIO_CHECK(count == 100);
}
template <typename CompletionToken>
auto throw_on_cancel(boost::asio::io_context& ctx, CompletionToken&& token)
{
return boost::asio::async_initiate<
CompletionToken, void(boost::system::error_code, int)>(
boost::asio::co_composed<
void(boost::system::error_code, int)>(
[](auto state, boost::asio::io_context& ctx) -> void
{
try
{
state.throw_if_cancelled(true);
int i = 0;
for (; i < 100; ++i)
co_await boost::asio::post(ctx, boost::asio::deferred);
co_return {boost::asio::error::eof, i};
}
catch (...)
{
co_return {boost::asio::error::invalid_argument, 42};
}
}, ctx), token, std::ref(ctx));
}
void test_throw_on_cancel()
{
boost::asio::io_context ctx(1);
int count = 0;
boost::system::error_code ec;
boost::asio::cancellation_signal cancel;
throw_on_cancel(ctx,
[&](boost::system::error_code e, int i)
{
ec = e;
count = i;
});
ctx.run();
BOOST_ASIO_CHECK(ec == boost::asio::error::eof);
BOOST_ASIO_CHECK(count == 100);
throw_on_cancel(ctx,
boost::asio::bind_cancellation_slot(cancel.slot(),
[&](boost::system::error_code e, int i)
{
ec = e;
count = i;
}));
ctx.restart();
ctx.run_one();
cancel.emit(boost::asio::cancellation_type::all);
ctx.run();
BOOST_ASIO_CHECK(ec == boost::asio::error::invalid_argument);
BOOST_ASIO_CHECK(count == 42);
throw_on_cancel(ctx,
boost::asio::bind_cancellation_slot(cancel.slot(),
[&](boost::system::error_code e, int i)
{
ec = e;
count = i;
}));
ctx.restart();
ctx.run();
BOOST_ASIO_CHECK(ec == boost::asio::error::eof);
BOOST_ASIO_CHECK(count == 100);
}
void test_no_signatures_detached()
{
boost::asio::io_context ctx(1);
int count1 = 0;
int count2 = 0;
boost::asio::async_initiate(
boost::asio::co_composed(
[](auto, boost::asio::io_context& ctx, int& count1, int& count2) -> void
{
for (;;)
{
++count1;
co_await boost::asio::post(ctx, boost::asio::deferred);
++count2;
}
}), boost::asio::detached, std::ref(ctx), std::ref(count1), std::ref(count2));
BOOST_ASIO_ASSERT(count1 == 1);
BOOST_ASIO_ASSERT(count2 == 0);
ctx.run_one();
BOOST_ASIO_ASSERT(count1 == 2);
BOOST_ASIO_ASSERT(count2 == 1);
ctx.restart();
ctx.run_one();
BOOST_ASIO_ASSERT(count1 == 3);
BOOST_ASIO_ASSERT(count2 == 2);
}
BOOST_ASIO_TEST_SUITE
(
"co_composed",
BOOST_ASIO_TEST_CASE(test_throw_first)
BOOST_ASIO_TEST_CASE(test_throw_after_await)
BOOST_ASIO_TEST_CASE(test_throw_in_first_suspend)
BOOST_ASIO_TEST_CASE(test_throw_in_suspend_after_await)
BOOST_ASIO_TEST_CASE(test_post_loop)
BOOST_ASIO_TEST_CASE(test_nested_post_loop)
BOOST_ASIO_TEST_CASE(test_post_loop_return_1_0)
BOOST_ASIO_TEST_CASE(test_post_loop_return_1_1)
BOOST_ASIO_TEST_CASE(test_post_loop_return_1_2)
BOOST_ASIO_TEST_CASE(test_post_loop_return_2)
BOOST_ASIO_TEST_CASE(test_complete_on_cancel)
BOOST_ASIO_TEST_CASE(test_complete_with_default_on_cancel)
BOOST_ASIO_TEST_CASE(test_throw_on_cancel)
BOOST_ASIO_TEST_CASE(test_no_signatures_detached)
)
#else // defined(BOOST_ASIO_HAS_CO_AWAIT)
BOOST_ASIO_TEST_SUITE
(
"co_composed",
BOOST_ASIO_TEST_CASE(null_test)
)
#endif // defined(BOOST_ASIO_HAS_CO_AWAIT)