]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/boost/outcome/detail/coroutine_support.ipp
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / boost / boost / outcome / detail / coroutine_support.ipp
1 /* Tells C++ coroutines about Outcome's result
2 (C) 2019-2022 Niall Douglas <http://www.nedproductions.biz/> (12 commits)
3 File Created: Oct 2019
4
5
6 Boost Software License - Version 1.0 - August 17th, 2003
7
8 Permission is hereby granted, free of charge, to any person or organization
9 obtaining a copy of the software and accompanying documentation covered by
10 this license (the "Software") to use, reproduce, display, distribute,
11 execute, and transmit the Software, and to prepare derivative works of the
12 Software, and to permit third-parties to whom the Software is furnished to
13 do so, all subject to the following:
14
15 The copyright notices in the Software and this entire statement, including
16 the above license grant, this restriction and the following disclaimer,
17 must be included in all copies of the Software, in whole or in part, and
18 all derivative works of the Software, unless such copies or derivative
19 works are solely in the form of machine-executable object code generated by
20 a source language processor.
21
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
25 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
26 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
27 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28 DEALINGS IN THE SOFTWARE.
29 */
30
31 #ifndef BOOST_OUTCOME_COROUTINE_SUPPORT_NAMESPACE_BEGIN
32 #error This header must only be included by outcome/coroutine_support.hpp or outcome/experimental/coroutine_support.hpp
33 #endif
34
35 #ifndef BOOST_OUTCOME_DETAIL_COROUTINE_SUPPORT_HPP
36 #define BOOST_OUTCOME_DETAIL_COROUTINE_SUPPORT_HPP
37
38 #include <atomic>
39 #include <cassert>
40
41 #if __cpp_impl_coroutine || (defined(_MSC_VER) && __cpp_coroutines) || (defined(__clang__) && __cpp_coroutines)
42 #ifndef BOOST_OUTCOME_HAVE_NOOP_COROUTINE
43 #if defined(__has_builtin)
44 #if __has_builtin(__builtin_coro_noop) || (!defined(__clang__) && __GNUC__ >= 10)
45 #define BOOST_OUTCOME_HAVE_NOOP_COROUTINE 1
46 #endif
47 #endif
48 #endif
49 #ifndef BOOST_OUTCOME_HAVE_NOOP_COROUTINE
50 #if _MSC_VER >= 1928 || (!defined(__clang__) && __GNUC__ >= 10)
51 #define BOOST_OUTCOME_HAVE_NOOP_COROUTINE 1
52 #else
53 #define BOOST_OUTCOME_HAVE_NOOP_COROUTINE 0
54 #endif
55 #endif
56 #if __has_include(<coroutine>)
57 #include <coroutine>
58 BOOST_OUTCOME_V2_NAMESPACE_BEGIN
59 namespace awaitables
60 {
61 template <class Promise = void> using coroutine_handle = std::coroutine_handle<Promise>;
62 template <class... Args> using coroutine_traits = std::coroutine_traits<Args...>;
63 using std::suspend_always;
64 using std::suspend_never;
65 #if BOOST_OUTCOME_HAVE_NOOP_COROUTINE
66 using std::noop_coroutine;
67 #endif
68 } // namespace awaitables
69 BOOST_OUTCOME_V2_NAMESPACE_END
70 #define BOOST_OUTCOME_FOUND_COROUTINE_HEADER 1
71 #elif __has_include(<experimental/coroutine>)
72 #include <experimental/coroutine>
73 BOOST_OUTCOME_V2_NAMESPACE_BEGIN
74 namespace awaitables
75 {
76 template <class Promise = void> using coroutine_handle = std::experimental::coroutine_handle<Promise>;
77 template <class... Args> using coroutine_traits = std::experimental::coroutine_traits<Args...>;
78 using std::experimental::suspend_always;
79 using std::experimental::suspend_never;
80 #if BOOST_OUTCOME_HAVE_NOOP_COROUTINE
81 using std::experimental::noop_coroutine;
82 #endif
83 } // namespace awaitables
84 BOOST_OUTCOME_V2_NAMESPACE_END
85 #define BOOST_OUTCOME_FOUND_COROUTINE_HEADER 1
86 #endif
87 #endif
88
89 BOOST_OUTCOME_V2_NAMESPACE_EXPORT_BEGIN
90 namespace awaitables
91 {
92 namespace detail
93 {
94 struct error_type_not_found
95 {
96 };
97 struct exception_type_not_found
98 {
99 };
100 template <class T> struct type_found
101 {
102 using type = T;
103 };
104 template <class T, class U = typename T::error_type> constexpr inline type_found<U> extract_error_type(int /*unused*/) { return {}; }
105 template <class T> constexpr inline type_found<error_type_not_found> extract_error_type(...) { return {}; }
106 template <class T, class U = typename T::exception_type> constexpr inline type_found<U> extract_exception_type(int /*unused*/) { return {}; }
107 template <class T> constexpr inline type_found<exception_type_not_found> extract_exception_type(...) { return {}; }
108
109 BOOST_OUTCOME_TEMPLATE(class T, class U)
110 BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(BOOST_OUTCOME_V2_NAMESPACE::detail::is_constructible<U, T>))
111 inline bool try_set_error(T &&e, U *result)
112 {
113 new(result) U(static_cast<T &&>(e));
114 return true;
115 }
116 template <class T> inline bool try_set_error(T && /*unused*/, ...) { return false; }
117 BOOST_OUTCOME_TEMPLATE(class T, class U)
118 BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(BOOST_OUTCOME_V2_NAMESPACE::detail::is_constructible<U, T>))
119 inline void set_or_rethrow(T &e, U *result) { new(result) U(e); }
120 template <class T> inline void set_or_rethrow(T &e, ...) { rethrow_exception(e); }
121 template <class T> class fake_atomic
122 {
123 T _v;
124
125 public:
126 constexpr fake_atomic(T v)
127 : _v(v)
128 {
129 }
130 T load(std::memory_order /*unused*/) { return _v; }
131 void store(T v, std::memory_order /*unused*/) { _v = v; }
132 };
133
134 #ifdef BOOST_OUTCOME_FOUND_COROUTINE_HEADER
135 template <class Awaitable, bool suspend_initial, bool use_atomic, bool is_void> struct outcome_promise_type
136 {
137 using container_type = typename Awaitable::container_type;
138 using result_set_type = std::conditional_t<use_atomic, std::atomic<bool>, fake_atomic<bool>>;
139 union
140 {
141 BOOST_OUTCOME_V2_NAMESPACE::detail::empty_type _default{};
142 container_type result;
143 };
144 result_set_type result_set{false};
145 coroutine_handle<> continuation;
146
147 outcome_promise_type() noexcept {}
148 outcome_promise_type(const outcome_promise_type &) = delete;
149 outcome_promise_type(outcome_promise_type &&) = delete;
150 outcome_promise_type &operator=(const outcome_promise_type &) = delete;
151 outcome_promise_type &operator=(outcome_promise_type &&) = delete;
152 ~outcome_promise_type()
153 {
154 if(result_set.load(std::memory_order_acquire))
155 {
156 result.~container_type(); // could throw
157 }
158 }
159 auto get_return_object()
160 {
161 return Awaitable{*this}; // could throw bad_alloc
162 }
163 void return_value(container_type &&value)
164 {
165 assert(!result_set.load(std::memory_order_acquire));
166 if(result_set.load(std::memory_order_acquire))
167 {
168 result.~container_type(); // could throw
169 }
170 new(&result) container_type(static_cast<container_type &&>(value)); // could throw
171 result_set.store(true, std::memory_order_release);
172 }
173 void return_value(const container_type &value)
174 {
175 assert(!result_set.load(std::memory_order_acquire));
176 if(result_set.load(std::memory_order_acquire))
177 {
178 result.~container_type(); // could throw
179 }
180 new(&result) container_type(value); // could throw
181 result_set.store(true, std::memory_order_release);
182 }
183 void unhandled_exception()
184 {
185 assert(!result_set.load(std::memory_order_acquire));
186 if(result_set.load(std::memory_order_acquire))
187 {
188 result.~container_type();
189 }
190 #ifndef BOOST_NO_EXCEPTIONS
191 auto e = std::current_exception();
192 auto ec = detail::error_from_exception(static_cast<decltype(e) &&>(e), {});
193 // Try to set error code first
194 if(!detail::error_is_set(ec) || !detail::try_set_error(static_cast<decltype(ec) &&>(ec), &result))
195 {
196 detail::set_or_rethrow(e, &result); // could throw
197 }
198 #else
199 std::terminate();
200 #endif
201 result_set.store(true, std::memory_order_release);
202 }
203 auto initial_suspend() noexcept
204 {
205 struct awaiter
206 {
207 bool await_ready() noexcept { return !suspend_initial; }
208 void await_resume() noexcept {}
209 void await_suspend(coroutine_handle<> /*unused*/) noexcept {}
210 };
211 return awaiter{};
212 }
213 auto final_suspend() noexcept
214 {
215 struct awaiter
216 {
217 bool await_ready() noexcept { return false; }
218 void await_resume() noexcept {}
219 #if BOOST_OUTCOME_HAVE_NOOP_COROUTINE
220 coroutine_handle<> await_suspend(coroutine_handle<outcome_promise_type> self) noexcept
221 {
222 return self.promise().continuation ? self.promise().continuation : noop_coroutine();
223 }
224 #else
225 void await_suspend(coroutine_handle<outcome_promise_type> self)
226 {
227 if(self.promise().continuation)
228 {
229 return self.promise().continuation.resume();
230 }
231 }
232 #endif
233 };
234 return awaiter{};
235 }
236 };
237 template <class Awaitable, bool suspend_initial, bool use_atomic> struct outcome_promise_type<Awaitable, suspend_initial, use_atomic, true>
238 {
239 using container_type = void;
240 using result_set_type = std::conditional_t<use_atomic, std::atomic<bool>, fake_atomic<bool>>;
241 result_set_type result_set{false};
242 coroutine_handle<> continuation;
243
244 outcome_promise_type() {}
245 outcome_promise_type(const outcome_promise_type &) = delete;
246 outcome_promise_type(outcome_promise_type &&) = delete;
247 outcome_promise_type &operator=(const outcome_promise_type &) = delete;
248 outcome_promise_type &operator=(outcome_promise_type &&) = delete;
249 ~outcome_promise_type() = default;
250 auto get_return_object()
251 {
252 return Awaitable{*this}; // could throw bad_alloc
253 }
254 void return_void() noexcept
255 {
256 assert(!result_set.load(std::memory_order_acquire));
257 result_set.store(true, std::memory_order_release);
258 }
259 void unhandled_exception()
260 {
261 assert(!result_set.load(std::memory_order_acquire));
262 std::rethrow_exception(std::current_exception()); // throws
263 }
264 auto initial_suspend() noexcept
265 {
266 struct awaiter
267 {
268 bool await_ready() noexcept { return !suspend_initial; }
269 void await_resume() noexcept {}
270 void await_suspend(coroutine_handle<> /*unused*/) noexcept {}
271 };
272 return awaiter{};
273 }
274 auto final_suspend() noexcept
275 {
276 struct awaiter
277 {
278 bool await_ready() noexcept { return false; }
279 void await_resume() noexcept {}
280 #if BOOST_OUTCOME_HAVE_NOOP_COROUTINE
281 coroutine_handle<> await_suspend(coroutine_handle<outcome_promise_type> self) noexcept
282 {
283 return self.promise().continuation ? self.promise().continuation : noop_coroutine();
284 }
285 #else
286 void await_suspend(coroutine_handle<outcome_promise_type> self)
287 {
288 if(self.promise().continuation)
289 {
290 return self.promise().continuation.resume();
291 }
292 }
293 #endif
294 };
295 return awaiter{};
296 }
297 };
298 template <class Awaitable, bool suspend_initial, bool use_atomic>
299 constexpr inline auto move_result_from_promise_if_not_void(outcome_promise_type<Awaitable, suspend_initial, use_atomic, false> &p)
300 {
301 return static_cast<typename Awaitable::container_type &&>(p.result);
302 }
303 template <class Awaitable, bool suspend_initial, bool use_atomic>
304 constexpr inline void move_result_from_promise_if_not_void(outcome_promise_type<Awaitable, suspend_initial, use_atomic, true> & /*unused*/)
305 {
306 }
307
308 template <class Cont, bool suspend_initial, bool use_atomic> struct BOOST_OUTCOME_NODISCARD awaitable
309 {
310 using container_type = Cont;
311 using promise_type = outcome_promise_type<awaitable, suspend_initial, use_atomic, std::is_void<container_type>::value>;
312 coroutine_handle<promise_type> _h;
313
314 awaitable(awaitable &&o) noexcept
315 : _h(static_cast<coroutine_handle<promise_type> &&>(o._h))
316 {
317 o._h = nullptr;
318 }
319 awaitable(const awaitable &o) = delete;
320 awaitable &operator=(awaitable &&) = delete; // as per P1056
321 awaitable &operator=(const awaitable &) = delete;
322 ~awaitable()
323 {
324 if(_h)
325 {
326 _h.destroy();
327 }
328 }
329 explicit awaitable(promise_type &p) // could throw
330 : _h(coroutine_handle<promise_type>::from_promise(p))
331 {
332 }
333 bool await_ready() noexcept { return _h.promise().result_set.load(std::memory_order_acquire); }
334 container_type await_resume()
335 {
336 assert(_h.promise().result_set.load(std::memory_order_acquire));
337 if(!_h.promise().result_set.load(std::memory_order_acquire))
338 {
339 std::terminate();
340 }
341 return detail::move_result_from_promise_if_not_void(_h.promise());
342 }
343 #if BOOST_OUTCOME_HAVE_NOOP_COROUTINE
344 coroutine_handle<> await_suspend(coroutine_handle<> cont) noexcept
345 {
346 _h.promise().continuation = cont;
347 return _h;
348 }
349 #else
350 void await_suspend(coroutine_handle<> cont)
351 {
352 _h.promise().continuation = cont;
353 _h.resume();
354 }
355 #endif
356 };
357 #endif
358 } // namespace detail
359
360 } // namespace awaitables
361
362 BOOST_OUTCOME_V2_NAMESPACE_END
363
364 #endif
365
366 #ifdef BOOST_OUTCOME_FOUND_COROUTINE_HEADER
367 BOOST_OUTCOME_COROUTINE_SUPPORT_NAMESPACE_EXPORT_BEGIN
368 /*! AWAITING HUGO JSON CONVERSION TOOL
369 SIGNATURE NOT RECOGNISED
370 */
371 template <class T> using eager = BOOST_OUTCOME_V2_NAMESPACE::awaitables::detail::awaitable<T, false, false>;
372
373 /*! AWAITING HUGO JSON CONVERSION TOOL
374 SIGNATURE NOT RECOGNISED
375 */
376 template <class T> using atomic_eager = BOOST_OUTCOME_V2_NAMESPACE::awaitables::detail::awaitable<T, false, true>;
377
378 /*! AWAITING HUGO JSON CONVERSION TOOL
379 SIGNATURE NOT RECOGNISED
380 */
381 template <class T> using lazy = BOOST_OUTCOME_V2_NAMESPACE::awaitables::detail::awaitable<T, true, false>;
382
383 /*! AWAITING HUGO JSON CONVERSION TOOL
384 SIGNATURE NOT RECOGNISED
385 */
386 template <class T> using atomic_lazy = BOOST_OUTCOME_V2_NAMESPACE::awaitables::detail::awaitable<T, true, true>;
387
388 BOOST_OUTCOME_COROUTINE_SUPPORT_NAMESPACE_END
389 #endif