1//
2// experimental/coro/exception.cpp
3// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4//
5// Copyright (c) 2021-2023 Klemens D. Morgenstern
6// (klemens dot morgenstern at gmx dot net)
7//
8// Distributed under the Boost Software License, Version 1.0. (See accompanying
9// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
10//
11
12// Disable autolinking for unit tests.
13#if !defined(BOOST_ALL_NO_LIB)
14#define BOOST_ALL_NO_LIB 1
15#endif // !defined(BOOST_ALL_NO_LIB)
16
17// Test that header file is self-contained.
18#include <boost/asio/experimental/coro.hpp>
19
20#include <boost/asio/co_spawn.hpp>
21#include <boost/asio/detached.hpp>
22#include <boost/asio/io_context.hpp>
23#include <boost/asio/use_awaitable.hpp>
24#include <boost/asio/awaitable.hpp>
25#include "../../unit_test.hpp"
26
27using namespace boost::asio::experimental;
28
29namespace coro {
30
31template <typename Func>
32struct on_scope_exit
33{
34 Func func;
35
36 static_assert(noexcept(func()));
37
38 on_scope_exit(const Func &f)
39 : func(static_cast< Func && >(f))
40 {
41 }
42
43 on_scope_exit(Func &&f)
44 : func(f)
45 {
46 }
47
48 on_scope_exit(const on_scope_exit &) = delete;
49
50 ~on_scope_exit()
51 {
52 func();
53 }
54};
55
56boost::asio::experimental::coro<int> throwing_generator(
57 boost::asio::any_io_executor, int &last, bool &destroyed)
58{
59 on_scope_exit x = [&]() noexcept { destroyed = true; };
60 (void)x;
61
62 int i = 0;
63 while (i < 3)
64 co_yield last = ++i;
65
66 throw std::runtime_error("throwing-generator");
67}
68
69boost::asio::awaitable<void> throwing_generator_test()
70{
71 int val = 0;
72 bool destr = false;
73 {
74 auto gi = throwing_generator(
75 co_await boost::asio::this_coro::executor,
76 val, destr);
77 bool caught = false;
78 try
79 {
80 for (int i = 0; i < 10; i++)
81 {
82 BOOST_ASIO_CHECK(val == i);
83 const auto next = co_await gi.async_resume(boost::asio::use_awaitable);
84 BOOST_ASIO_CHECK(next);
85 BOOST_ASIO_CHECK(val == *next);
86 BOOST_ASIO_CHECK(val == i + 1);
87 }
88 }
89 catch (std::runtime_error &err)
90 {
91 caught = true;
92 using std::operator ""sv;
93 BOOST_ASIO_CHECK(err.what() == "throwing-generator"sv);
94 }
95 BOOST_ASIO_CHECK(val == 3);
96 BOOST_ASIO_CHECK(caught);
97 }
98 BOOST_ASIO_CHECK(destr);
99};
100
101void run_throwing_generator_test()
102{
103 boost::asio::io_context ctx;
104 boost::asio::co_spawn(ctx, throwing_generator_test(), boost::asio::detached);
105 ctx.run();
106}
107
108boost::asio::experimental::coro<int(int)> throwing_stacked(
109 boost::asio::any_io_executor exec, int &val,
110 bool &destroyed_inner, bool &destroyed)
111{
112 BOOST_ASIO_CHECK((co_await boost::asio::this_coro::throw_if_cancelled()));
113
114 on_scope_exit x = [&]() noexcept { destroyed = true; };
115 (void)x;
116
117 auto gen = throwing_generator(exec, last&: val, destroyed&: destroyed_inner);
118 while (auto next = co_await gen) // 1, 2, 4, 8, ...
119 BOOST_ASIO_CHECK(42 ==(co_yield *next)); // offset is delayed by one cycle
120}
121
122boost::asio::awaitable<void> throwing_generator_stacked_test()
123{
124 int val = 0;
125 bool destr = false, destr_inner = false;
126 {
127 auto gi = throwing_stacked(
128 co_await boost::asio::this_coro::executor,
129 val, destr, destr_inner);
130 bool caught = false;
131 try
132 {
133 for (int i = 0; i < 10; i++)
134 {
135 BOOST_ASIO_CHECK(val == i);
136 const auto next =
137 co_await gi.async_resume(42, boost::asio::use_awaitable);
138 BOOST_ASIO_CHECK(next);
139 BOOST_ASIO_CHECK(val == *next);
140 BOOST_ASIO_CHECK(val == i + 1);
141 }
142 }
143 catch (std::runtime_error &err)
144 {
145 caught = true;
146 using std::operator ""sv;
147 BOOST_ASIO_CHECK(err.what() == "throwing-generator"sv);
148 }
149 BOOST_ASIO_CHECK(val == 3);
150 BOOST_ASIO_CHECK(caught);
151 }
152 BOOST_ASIO_CHECK(destr);
153 BOOST_ASIO_CHECK(destr_inner);
154};
155
156void run_throwing_generator_stacked_test()
157{
158 boost::asio::io_context ctx;
159 boost::asio::co_spawn(ctx,
160 throwing_generator_stacked_test,
161 boost::asio::detached);
162 ctx.run();
163}
164
165} // namespace coro
166
167BOOST_ASIO_TEST_SUITE
168(
169 "coro/exception",
170 BOOST_ASIO_TEST_CASE(::coro::run_throwing_generator_stacked_test)
171 BOOST_ASIO_TEST_CASE(::coro::run_throwing_generator_test)
172)
173

source code of boost/libs/asio/test/experimental/coro/exception.cpp