1//
2// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net)
3//
4// Distributed under the Boost Software License, Version 1.0. (See accompanying
5// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6//
7
8#include <boost/cobalt/task.hpp>
9#include <boost/cobalt/main.hpp>
10#include <boost/cobalt/op.hpp>
11#include <boost/cobalt/spawn.hpp>
12#include <boost/cobalt/join.hpp>
13
14#include <boost/test/unit_test.hpp>
15#include "test.hpp"
16#include <boost/asio/detached.hpp>
17#include <boost/asio/steady_timer.hpp>
18#include <boost/asio/awaitable.hpp>
19#include <boost/asio/bind_cancellation_slot.hpp>
20#include <boost/asio/co_spawn.hpp>
21#include <boost/asio/strand.hpp>
22#include <boost/asio/thread_pool.hpp>
23
24#include <boost/core/ignore_unused.hpp>
25
26#include <new>
27
28using namespace boost;
29
30BOOST_AUTO_TEST_SUITE(task);
31
32namespace
33{
34
35cobalt::task<void> test0()
36{
37 co_return;
38}
39
40cobalt::task<double> test2(int i)
41{
42 co_await test0();
43 co_return i;
44}
45
46cobalt::task<int> test1()
47{
48 co_await test2(i: 42);
49 co_await test2(i: 42);
50
51
52 co_await asio::post(ex: co_await cobalt::this_coro::executor, token: cobalt::use_task);
53 co_return 452;
54}
55}
56
57BOOST_AUTO_TEST_CASE(test_1)
58{
59
60 bool done = false;
61
62 asio::io_context ctx;
63 asio::steady_timer tim{ctx};
64 cobalt::this_thread::set_executor(ctx.get_executor());
65
66 cobalt::spawn(
67 executor: ctx.get_executor(),
68 t: test1(),
69 token: [&](std::exception_ptr ex, int res)
70 {
71 BOOST_CHECK(ex == nullptr);
72 BOOST_CHECK(res == 452);
73 done = true;
74 });
75
76 ctx.run();
77 BOOST_CHECK(done);
78}
79
80CO_TEST_CASE(cobalt_1)
81{
82 co_await test1();
83 co_return;
84}
85
86
87static cobalt::task<void> should_unwind(asio::io_context & ctx)
88{
89 co_await asio::post(ctx, token: cobalt::use_op);
90}
91
92BOOST_AUTO_TEST_CASE(unwind)
93{
94 asio::io_context ctx;
95 boost::cobalt::this_thread::set_executor(ctx.get_executor());
96 boost::ignore_unused(should_unwind(ctx));
97}
98namespace
99{
100
101
102cobalt::task<int> return_([[maybe_unused]] std::size_t ms,
103 asio::executor_arg_t, boost::cobalt::executor )
104{
105 co_return 1234u;
106}
107
108cobalt::task<void> delay_r(asio::io_context &ctx, std::size_t ms)
109{
110 auto tim = cobalt::use_op.as_default_on(object: asio::steady_timer(ctx, std::chrono::milliseconds{ms}));
111 co_await tim.async_wait();
112}
113
114cobalt::task<void> throw_()
115{
116 throw std::runtime_error("throw_");
117 co_return ;
118}
119
120cobalt::task<void> throw_post()
121{
122 co_await asio::post(ex: cobalt::this_thread::get_executor(), token: cobalt::use_op);
123 throw std::runtime_error("throw_");
124 co_return ;
125}
126
127}
128
129
130BOOST_AUTO_TEST_CASE(cancel_void)
131{
132 asio::io_context ctx;
133 cobalt::this_thread::set_executor(ctx.get_executor());
134 asio::cancellation_signal signal;
135
136
137 spawn(context&: ctx, t: delay_r(ctx, ms: 10000u), token: asio::bind_cancellation_slot(
138 s: signal.slot(),
139 t: [](std::exception_ptr ep)
140 {
141 BOOST_CHECK(ep != nullptr);
142 }));
143
144 asio::post(ctx, token: [&]{signal.emit(type: asio::cancellation_type::all);});
145
146 spawn(context&: ctx, t: return_(ms: 1234u, asio::executor_arg, ctx.get_executor()),
147 token: [](std::exception_ptr ep, std::size_t n)
148 {
149 BOOST_CHECK(ep == nullptr);
150 BOOST_CHECK(n == 1234u);
151 });
152
153
154 ctx.run();
155}
156
157static cobalt::task<void> delay_v(asio::io_context &ctx, std::size_t ms)
158{
159 asio::steady_timer tim(ctx, std::chrono::milliseconds{ms});
160 co_await tim.async_wait(token: cobalt::use_op);
161}
162
163
164BOOST_AUTO_TEST_CASE(cancel_int)
165{
166 asio::io_context ctx;
167 cobalt::this_thread::set_executor(ctx.get_executor());
168 asio::cancellation_signal signal;
169
170 spawn(context&: ctx, t: delay_v(ctx, ms: 10000u), token: asio::bind_cancellation_slot(
171 s: signal.slot(),
172 t: [](std::exception_ptr ep)
173 {
174 BOOST_CHECK(ep != nullptr);
175 }));
176
177 asio::post(ctx, token: [&]{signal.emit(type: asio::cancellation_type::all);});
178 spawn(context&: ctx, t: throw_(),
179 token: [](std::exception_ptr ep)
180 {
181 BOOST_CHECK(ep != nullptr);
182 });
183
184
185 ctx.run();
186}
187
188
189BOOST_AUTO_TEST_CASE(throw_cpl)
190{
191 asio::io_context ctx;
192 cobalt::this_thread::set_executor(ctx.get_executor());
193 asio::cancellation_signal signal;
194
195 spawn(context&: ctx, t: throw_(),
196 token: [](std::exception_ptr ep)
197 {
198 std::rethrow_exception(ep);
199 });
200
201
202 BOOST_CHECK_THROW(ctx.run(), std::runtime_error);
203}
204
205BOOST_AUTO_TEST_CASE(throw_cpl_delay)
206{
207 asio::io_context ctx;
208 cobalt::this_thread::set_executor(ctx.get_executor());
209 asio::cancellation_signal signal;
210
211 spawn(context&: ctx, t: throw_post(),
212 token: [](std::exception_ptr ep)
213 {
214 std::rethrow_exception(ep);
215 });
216
217
218 BOOST_CHECK_THROW(ctx.run(), std::runtime_error);
219}
220
221
222CO_TEST_CASE(stop_)
223{
224 BOOST_CHECK_THROW(
225 co_await
226 []() -> cobalt::task<int>
227 {
228 co_await stop();
229 co_return 42;
230 }(), boost::system::system_error);
231}
232
233
234cobalt::task<void> throw_if_test(asio::cancellation_signal & sig)
235{
236
237 BOOST_CHECK(co_await cobalt::this_coro::cancelled
238 == asio::cancellation_type::none);
239 sig.emit(type: asio::cancellation_type::terminal);
240 BOOST_CHECK(co_await cobalt::this_coro::cancelled
241 == asio::cancellation_type::terminal);
242 BOOST_CHECK_THROW(co_await asio::post(cobalt::use_op), boost::system::system_error);
243}
244
245
246BOOST_AUTO_TEST_CASE(throw_if_cancelled)
247{
248 asio::cancellation_signal sig;
249
250 asio::io_context ctx;
251 boost::cobalt::this_thread::set_executor(ctx.get_executor());
252 cobalt::spawn(context&: ctx, t: throw_if_test(sig),
253 token: asio::bind_cancellation_slot(s: sig.slot(), t: asio::detached));
254 ctx.run();
255}
256
257CO_TEST_CASE(reawait)
258{
259 auto t = test0();
260 co_await std::move(t);
261 BOOST_CHECK_NO_THROW(co_await std::move(t));
262}
263
264
265cobalt::task<int> test_strand1(asio::any_io_executor exec)
266{
267 BOOST_ASSERT(exec == co_await cobalt::this_coro::executor);
268 co_await asio::post(ex: co_await cobalt::this_coro::executor, token: cobalt::use_task);
269 co_return 31;
270}
271
272cobalt::task<void> test_strand()
273{
274 auto e = co_await cobalt::this_coro::executor;
275 co_await cobalt::join(p: test_strand1(exec: e), p: test_strand1(exec: e), p: test_strand1(exec: e), p: test_strand1(exec: e));
276}
277
278#if !defined(BOOST_COBALT_USE_IO_CONTEXT)
279
280BOOST_AUTO_TEST_CASE(stranded)
281{
282
283 asio::thread_pool ctx;
284 boost::cobalt::this_thread::set_executor(ctx.get_executor());
285 cobalt::spawn(executor: asio::make_strand(ex: ctx.get_executor()), t: test_strand(),token: [](std::exception_ptr ep) { if (ep) std::rethrow_exception(ep); });
286 cobalt::spawn(executor: asio::make_strand(ex: ctx.get_executor()), t: test_strand(),token: [](std::exception_ptr ep) { if (ep) std::rethrow_exception(ep); });
287 cobalt::spawn(executor: asio::make_strand(ex: ctx.get_executor()), t: test_strand(),token: [](std::exception_ptr ep) { if (ep) std::rethrow_exception(ep); });
288 cobalt::spawn(executor: asio::make_strand(ex: ctx.get_executor()), t: test_strand(),token: [](std::exception_ptr ep) { if (ep) std::rethrow_exception(ep); });
289 cobalt::spawn(executor: asio::make_strand(ex: ctx.get_executor()), t: test_strand(),token: [](std::exception_ptr ep) { if (ep) std::rethrow_exception(ep); });
290 cobalt::spawn(executor: asio::make_strand(ex: ctx.get_executor()), t: test_strand(),token: [](std::exception_ptr ep) { if (ep) std::rethrow_exception(ep); });
291 cobalt::spawn(executor: asio::make_strand(ex: ctx.get_executor()), t: test_strand(),token: [](std::exception_ptr ep) { if (ep) std::rethrow_exception(ep); });
292 cobalt::spawn(executor: asio::make_strand(ex: ctx.get_executor()), t: test_strand(),token: [](std::exception_ptr ep) { if (ep) std::rethrow_exception(ep); });
293 BOOST_CHECK_NO_THROW(ctx.join());
294}
295
296#endif
297
298struct task_move_only
299{
300 task_move_only() = default;
301 task_move_only(task_move_only &&) = default;
302 task_move_only & operator=(task_move_only &&) = delete;
303};
304
305cobalt::task<task_move_only> task_move_only_test()
306{
307 co_return task_move_only{};
308}
309
310CO_TEST_CASE(move_only)
311{
312 co_await task_move_only_test();
313}
314
315#if !defined(BOOST_COBALT_USE_IO_CONTEXT) && !defined(BOOST_COBALT_CUSTOM_EXECUTOR)
316
317BOOST_AUTO_TEST_CASE(cancel_task_)
318{
319 asio::thread_pool ctx{1};
320 asio::cancellation_signal sig;
321 cobalt::spawn(context&: ctx, t: test0(), token: asio::bind_cancellation_slot(s: sig.slot(), t: asio::detached));
322 sig.emit(type: asio::cancellation_type::all);
323 ctx.join();
324}
325
326#endif
327
328
329
330BOOST_AUTO_TEST_SUITE_END();

source code of boost/libs/cobalt/test/task.cpp