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/op.hpp>
9#include <boost/cobalt/spawn.hpp>
10#include <boost/cobalt/promise.hpp>
11
12#include <boost/asio/detached.hpp>
13#include <boost/asio/experimental/channel.hpp>
14#include <boost/asio/steady_timer.hpp>
15
16#include <boost/test/unit_test.hpp>
17#include "test.hpp"
18
19using namespace boost;
20
21BOOST_AUTO_TEST_SUITE(op);
22
23template<typename Timer>
24struct test_wait_op : cobalt::op<system::error_code>
25{
26 Timer & tim;
27
28 test_wait_op(Timer & tim) : tim(tim) {}
29
30 void ready(cobalt::handler<system::error_code> h)
31 {
32 if (tim.expiry() < Timer::clock_type::now())
33 h({});
34 }
35 void initiate(cobalt::completion_handler<system::error_code> complete)
36 {
37 tim.async_wait(std::move(complete));
38 }
39};
40
41template<typename Timer>
42struct test_wait_op_2 : cobalt::op<system::error_code>
43{
44 Timer & tim;
45
46 test_wait_op_2(Timer & tim) : tim(tim) {}
47
48 void ready(cobalt::handler<system::error_code> h)
49 {
50 if (tim.expiry() < Timer::clock_type::now())
51 h(system::error_code(asio::error::operation_aborted));
52 }
53 void initiate(cobalt::completion_handler<system::error_code> complete)
54 {
55 tim.async_wait(std::move(complete));
56 }
57};
58
59
60struct post_op : cobalt::op<>
61{
62 asio::any_io_executor exec;
63
64 post_op(asio::any_io_executor exec) : exec(exec) {}
65
66 void initiate(cobalt::completion_handler<> complete)
67 {
68 asio::post(token: std::move(complete));
69 }
70};
71
72
73CO_TEST_CASE(op)
74{
75
76 asio::steady_timer tim{co_await asio::this_coro::executor, std::chrono::milliseconds(10)};
77
78 co_await test_wait_op{tim};
79 co_await test_wait_op{tim};
80
81 tim.expires_after(expiry_time: std::chrono::milliseconds(10));
82
83 co_await test_wait_op_2{tim};
84 BOOST_CHECK_THROW(co_await test_wait_op_2{tim}, boost::system::system_error);
85
86 (co_await cobalt::as_result(aw: post_op(co_await asio::this_coro::executor))).value();
87 (co_await cobalt::as_result(aw: tim.async_wait(token: cobalt::use_op))).value();
88}
89
90struct op_throw_op
91{
92 template<typename Handler>
93 void operator()(Handler &&)
94 {
95 throw std::runtime_error("test-exception");
96
97 }
98};
99
100template<typename CompletionToken>
101auto op_throw(CompletionToken&& token)
102{
103 return asio::async_initiate<CompletionToken, void(std::exception_ptr)>(
104 op_throw_op{}, token);
105}
106
107
108BOOST_AUTO_TEST_CASE(op_throw_)
109{
110
111 auto val = [&]() -> cobalt::task<void> {BOOST_CHECK_THROW(co_await op_throw(cobalt::use_op), boost::system::system_error);};
112
113 asio::io_context ctx;
114 cobalt::this_thread::set_executor(ctx.get_executor());
115 BOOST_CHECK_NO_THROW(spawn(ctx, val(), asio::detached));
116
117BOOST_CHECK_NO_THROW(ctx.run());
118}
119
120struct throw_op : cobalt::op<std::exception_ptr>
121{
122 asio::any_io_executor exec;
123
124 throw_op(asio::any_io_executor exec) : exec(exec) {}
125
126 void initiate(cobalt::completion_handler<std::exception_ptr> complete)
127 {
128 asio::post(ex: exec, token: asio::append(completion_token: std::move(complete), values: std::make_exception_ptr(ex: std::runtime_error("test-exception"))));
129 }
130};
131
132
133CO_TEST_CASE(exception_op)
134try
135{
136 BOOST_CHECK_THROW(co_await throw_op(co_await asio::this_coro::executor), boost::system::system_error);
137}
138catch(...) {}
139
140
141struct initiate_op : cobalt::op<>
142{
143 asio::any_io_executor exec;
144
145 initiate_op(asio::any_io_executor exec) : exec(exec) {}
146
147 void initiate(cobalt::completion_handler<> complete)
148 {
149 throw std::runtime_error("test-exception");
150 asio::post(ex: exec, token: std::move(complete));
151 }
152};
153
154
155CO_TEST_CASE(initiate_exception_op)
156try
157{
158 BOOST_CHECK_THROW(co_await throw_op(co_await asio::this_coro::executor), boost::system::system_error);
159} catch(...) {}
160
161CO_TEST_CASE(immediate_executor)
162{
163 auto called = false;
164 asio::post(ex: co_await asio::this_coro::executor, token: [&]{called = true;});
165 asio::experimental::channel<void(system::error_code)> chn{co_await asio::this_coro::executor, 2u};
166 co_await chn.async_send(args: system::error_code(), token: cobalt::use_op);
167 auto [ec] = co_await cobalt::as_tuple(aw: chn.async_receive(token: cobalt::use_op));
168 BOOST_CHECK(!ec);
169
170 BOOST_CHECK(!called);
171 co_await cobalt::as_tuple(aw: asio::post(ex: co_await asio::this_coro::executor, token: cobalt::use_op));
172 BOOST_CHECK(called);
173}
174
175struct test_async_initiate
176{
177
178 template<typename Handler>
179 void operator()(Handler && h, std::shared_ptr<int> ptr)
180 {
181 BOOST_CHECK(ptr);
182 asio::dispatch(
183 asio::get_associated_immediate_executor(
184 h, asio::get_associated_executor(h)),
185 std::move(h));
186 }
187};
188
189template<typename Token>
190auto test_cobalt(std::shared_ptr<int> & ptr, Token && token)
191{
192 return asio::async_initiate<Token, void()>(test_async_initiate{}, token, ptr);
193}
194
195CO_TEST_CASE(no_move_from)
196{
197 std::shared_ptr<int> p = std::make_shared<int>();
198 BOOST_CHECK(p);
199 co_await test_cobalt(ptr&: p, token: cobalt::use_op);
200 BOOST_CHECK(p);
201}
202
203
204
205
206BOOST_AUTO_TEST_SUITE_END();

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