]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/boost/asio/experimental/coro.hpp
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / boost / boost / asio / experimental / coro.hpp
CommitLineData
1e59de90
TL
1//
2// experimental/coro.hpp
3// ~~~~~~~~~~~~~~~~~~~~~
4//
5// Copyright (c) 2021-2022 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#ifndef BOOST_ASIO_EXPERIMENTAL_CORO_HPP
13#define BOOST_ASIO_EXPERIMENTAL_CORO_HPP
14
15#if defined(_MSC_VER) && (_MSC_VER >= 1200)
16# pragma once
17#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
18
19#include <boost/asio/detail/config.hpp>
20#include <boost/asio/dispatch.hpp>
21#include <boost/asio/error.hpp>
22#include <boost/system/error_code.hpp>
23#include <boost/asio/experimental/coro_traits.hpp>
24#include <boost/asio/experimental/detail/coro_promise_allocator.hpp>
25#include <boost/asio/experimental/detail/partial_promise.hpp>
26#include <boost/asio/experimental/use_coro.hpp>
27#include <boost/asio/post.hpp>
28
29#include <boost/asio/detail/push_options.hpp>
30
31namespace boost {
32namespace asio {
33namespace experimental {
34namespace detail {
35
36template <typename T, typename Coroutine>
37struct coro_with_arg;
38
39} // namespace detail
40
41/// The main type of a resumable coroutine.
42/**
43 * Template parameter @c Yield specifies type or signature used by co_yield,
44 * @c Return specifies the type used for co_return, and @c Executor specifies
45 * the underlying executor type.
46 */
47template <typename Yield = void, typename Return = void,
48 typename Executor = any_io_executor>
49struct coro
50{
51 /// The traits of the coroutine. See boost::asio::experimental::coro_traits
52 /// for details.
53 using traits = coro_traits<Yield, Return, Executor>;
54
55 /// The value that can be passed into a symmetrical cororoutine. @c void if
56 /// asymmetrical.
57 using input_type = typename traits::input_type;
58
59 /// The type that can be passed out through a co_yield.
60 using yield_type = typename traits::yield_type;
61
62 /// The type that can be passed out through a co_return.
63 using return_type = typename traits::return_type;
64
65 /// The type received by a co_await or async_resume. Its a combination of
66 /// yield and return.
67 using result_type = typename traits::result_type;
68
69 /// The signature used by the async_resume.
70 using signature_type = typename traits::signature_type;
71
72 /// Whether or not the coroutine is noexcept.
73 constexpr static bool is_noexcept = traits::is_noexcept;
74
75 /// The error type of the coroutine. Void for noexcept
76 using error_type = typename traits::error_type;
77
78 /// Completion handler type used by async_resume.
79 using completion_handler = typename traits::completion_handler;
80
81 /// The internal promise-type of the coroutine.
82 using promise_type = detail::coro_promise<Yield, Return, Executor>;
83
84#if !defined(GENERATING_DOCUMENTATION)
85 template <typename T, typename Coroutine>
86 friend struct detail::coro_with_arg;
87#endif // !defined(GENERATING_DOCUMENTATION)
88
89 /// The executor type.
90 using executor_type = Executor;
91
92#if !defined(GENERATING_DOCUMENTATION)
93 friend struct detail::coro_promise<Yield, Return, Executor>;
94#endif // !defined(GENERATING_DOCUMENTATION)
95
96 /// The default constructor, gives an invalid coroutine.
97 coro() = default;
98
99 /// Move constructor.
100 coro(coro&& lhs) noexcept
101 : coro_(std::exchange(lhs.coro_, nullptr))
102 {
103 }
104
105 coro(const coro &) = delete;
106
107 /// Move assignment.
108 coro& operator=(coro&& lhs) noexcept
109 {
110 std::swap(coro_, lhs.coro_);
111 return *this;
112 }
113
114 coro& operator=(const coro&) = delete;
115
116 /// Destructor. Destroys the coroutine, if it holds a valid one.
117 /**
118 * @note This does not cancel an active coroutine. Destructing a resumable
119 * coroutine, i.e. one with a call to async_resume that has not completed, is
120 * undefined behaviour.
121 */
122 ~coro()
123 {
124 if (coro_ != nullptr)
125 {
126 struct destroyer
127 {
128 detail::coroutine_handle<promise_type> handle;
129
130 destroyer(const detail::coroutine_handle<promise_type>& handle)
131 : handle(handle)
132 { }
133
134 destroyer(destroyer&& lhs)
135 : handle(std::exchange(lhs.handle, nullptr))
136 {
137 }
138
139 destroyer(const destroyer&) = delete;
140
141 void operator()() {}
142
143 ~destroyer()
144 {
145 if (handle)
146 handle.destroy();
147 }
148 };
149
150 auto handle =
151 detail::coroutine_handle<promise_type>::from_promise(*coro_);
152 if (handle)
153 boost::asio::dispatch(coro_->get_executor(), destroyer{handle});
154 }
155 }
156
157 /// Get the used executor.
158 executor_type get_executor() const
159 {
160 if (coro_)
161 return coro_->get_executor();
162
163 if constexpr (std::is_default_constructible_v<Executor>)
164 return Executor{};
165 else
166 throw std::logic_error("Coroutine has no executor");
167 }
168
169 /// Resume the coroutine.
170 /**
171 * @param token The completion token of the async resume.
172 *
173 * @attention Calling an invalid coroutine with a noexcept signature is
174 * undefined behaviour.
175 *
176 * @note This overload is only available for coroutines without an input
177 * value.
178 */
179 template <typename CompletionToken>
180 requires std::is_void_v<input_type>
181 auto async_resume(CompletionToken&& token) &
182 {
183 return async_initiate<CompletionToken,
184 typename traits::completion_handler>(
185 initiate_async_resume(this), token);
186 }
187
188 /// Resume the coroutine.
189 /**
190 * @param token The completion token of the async resume.
191 *
192 * @attention Calling an invalid coroutine with a noexcept signature is
193 * undefined behaviour.
194 *
195 * @note This overload is only available for coroutines with an input value.
196 */
197 template <typename CompletionToken, detail::convertible_to<input_type> T>
198 auto async_resume(T&& ip, CompletionToken&& token) &
199 {
200 return async_initiate<CompletionToken,
201 typename traits::completion_handler>(
202 initiate_async_resume(this), token, std::forward<T>(ip));
203 }
204
205 /// Operator used for coroutines without input value.
206 auto operator co_await() requires (std::is_void_v<input_type>)
207 {
208 return awaitable_t{*this};
209 }
210
211 /// Operator used for coroutines with input value.
212 /**
213 * @param ip The input value
214 *
215 * @returns An awaitable handle.
216 *
217 * @code
218 * coro<void> push_values(coro<double(int)> c)
219 * {
220 * std::optional<double> res = co_await c(42);
221 * }
222 * @endcode
223 */
224 template <detail::convertible_to<input_type> T>
225 auto operator()(T&& ip)
226 {
227 return detail::coro_with_arg<std::decay_t<T>, coro>{
228 std::forward<T>(ip), *this};
229 }
230
231 /// Check whether the coroutine is open, i.e. can be resumed.
232 bool is_open() const
233 {
234 if (coro_)
235 {
236 auto handle =
237 detail::coroutine_handle<promise_type>::from_promise(*coro_);
238 return handle && !handle.done();
239 }
240 else
241 return false;
242 }
243
244 /// Check whether the coroutine is open, i.e. can be resumed.
245 explicit operator bool() const { return is_open(); }
246
247private:
248 struct awaitable_t;
249
250 struct initiate_async_resume;
251
252 explicit coro(promise_type* const cr) : coro_(cr) {}
253
254 promise_type* coro_{nullptr};
255};
256
257} // namespace experimental
258} // namespace asio
259} // namespace boost
260
261#include <boost/asio/detail/pop_options.hpp>
262
263#include <boost/asio/experimental/impl/coro.hpp>
264
265#endif // BOOST_ASIO_EXPERIMENTAL_CORO_HPP