1/* Tells C++ coroutines about Outcome's result
2(C) 2019-2024 Niall Douglas <http://www.nedproductions.biz/> (12 commits)
3File Created: Oct 2019
4
5
6Boost Software License - Version 1.0 - August 17th, 2003
7
8Permission is hereby granted, free of charge, to any person or organization
9obtaining a copy of the software and accompanying documentation covered by
10this license (the "Software") to use, reproduce, display, distribute,
11execute, and transmit the Software, and to prepare derivative works of the
12Software, and to permit third-parties to whom the Software is furnished to
13do so, all subject to the following:
14
15The copyright notices in the Software and this entire statement, including
16the above license grant, this restriction and the following disclaimer,
17must be included in all copies of the Software, in whole or in part, and
18all derivative works of the Software, unless such copies or derivative
19works are solely in the form of machine-executable object code generated by
20a source language processor.
21
22THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
25SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
26FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
27ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28DEALINGS 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 <exception>
40
41#ifndef BOOST_OUTCOME_COROUTINE_HEADER_TYPE
42#if __has_include(<coroutine>)
43#define BOOST_OUTCOME_COROUTINE_HEADER_TYPE 1
44#elif __has_include(<experimental/coroutine>)
45#define BOOST_OUTCOME_COROUTINE_HEADER_TYPE 2
46#else
47#define BOOST_OUTCOME_COROUTINE_HEADER_TYPE 0
48#endif
49#endif
50
51#if BOOST_OUTCOME_COROUTINE_HEADER_TYPE && (__cpp_impl_coroutine || (defined(_MSC_VER) && __cpp_coroutines))
52#ifndef BOOST_OUTCOME_HAVE_NOOP_COROUTINE
53#if defined(__has_builtin)
54#if __has_builtin(__builtin_coro_noop) || (!defined(__clang__) && __GNUC__ >= 10)
55#define BOOST_OUTCOME_HAVE_NOOP_COROUTINE 1
56#endif
57#endif
58#endif
59#ifndef BOOST_OUTCOME_HAVE_NOOP_COROUTINE
60#if _MSC_VER >= 1928 || (!defined(__clang__) && __GNUC__ >= 10)
61#define BOOST_OUTCOME_HAVE_NOOP_COROUTINE 1
62#else
63#define BOOST_OUTCOME_HAVE_NOOP_COROUTINE 0
64#endif
65#endif
66#if BOOST_OUTCOME_COROUTINE_HEADER_TYPE == 1
67#include <coroutine>
68BOOST_OUTCOME_V2_NAMESPACE_BEGIN
69namespace awaitables
70{
71 template <class Promise = void> using coroutine_handle = std::coroutine_handle<Promise>;
72 template <class... Args> using coroutine_traits = std::coroutine_traits<Args...>;
73 using std::suspend_always;
74 using std::suspend_never;
75#if BOOST_OUTCOME_HAVE_NOOP_COROUTINE
76 using std::noop_coroutine;
77#endif
78} // namespace awaitables
79BOOST_OUTCOME_V2_NAMESPACE_END
80#define BOOST_OUTCOME_FOUND_COROUTINE_HEADER 1
81#elif BOOST_OUTCOME_COROUTINE_HEADER_TYPE == 2
82#include <experimental/coroutine>
83BOOST_OUTCOME_V2_NAMESPACE_BEGIN
84namespace awaitables
85{
86 template <class Promise = void> using coroutine_handle = std::experimental::coroutine_handle<Promise>;
87 template <class... Args> using coroutine_traits = std::experimental::coroutine_traits<Args...>;
88 using std::experimental::suspend_always;
89 using std::experimental::suspend_never;
90#if BOOST_OUTCOME_HAVE_NOOP_COROUTINE
91 using std::experimental::noop_coroutine;
92#endif
93} // namespace awaitables
94BOOST_OUTCOME_V2_NAMESPACE_END
95#define BOOST_OUTCOME_FOUND_COROUTINE_HEADER 1
96#endif
97#endif
98
99#ifndef BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER
100// #include <iostream>
101// #define BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(...) std::cout << __VA_ARGS__ << std::endl;
102#define BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(...)
103#endif
104
105BOOST_OUTCOME_V2_NAMESPACE_EXPORT_BEGIN
106namespace awaitables
107{
108 namespace detail
109 {
110 struct error_type_not_found
111 {
112 };
113 struct exception_type_not_found
114 {
115 };
116 template <class T> struct type_found
117 {
118 using type = T;
119 };
120 template <class T, class U = typename T::error_type> constexpr inline type_found<U> extract_error_type(int /*unused*/)
121 {
122 return {};
123 }
124 template <class T> constexpr inline type_found<error_type_not_found> extract_error_type(...)
125 {
126 return {};
127 }
128 template <class T, class U = typename T::exception_type> constexpr inline type_found<U> extract_exception_type(int /*unused*/)
129 {
130 return {};
131 }
132 template <class T> constexpr inline type_found<exception_type_not_found> extract_exception_type(...)
133 {
134 return {};
135 }
136
137 BOOST_OUTCOME_TEMPLATE(class T, class U)
138 BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(BOOST_OUTCOME_V2_NAMESPACE::detail::is_constructible<U, T>))
139 inline bool try_set_error(T &&e, U *result)
140 {
141 new(result) U(static_cast<T &&>(e));
142 return true;
143 }
144 template <class T> inline bool try_set_error(T && /*unused*/, ...)
145 {
146 return false;
147 }
148 BOOST_OUTCOME_TEMPLATE(class T, class U)
149 BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(BOOST_OUTCOME_V2_NAMESPACE::detail::is_constructible<U, T>))
150 inline void set_or_rethrow(T &e, U *result)
151 {
152 new(result) U(e);
153 }
154 template <class T> inline void set_or_rethrow(T &e, ...)
155 {
156 rethrow_exception(e);
157 }
158 template <class T> class fake_atomic
159 {
160 T _v;
161
162 public:
163 constexpr fake_atomic(T v)
164 : _v(v)
165 {
166 }
167 T load(std::memory_order /*unused*/) { return _v; }
168 void store(T v, std::memory_order /*unused*/) { _v = v; }
169 bool compare_exchange_strong(T &expected, T v, std::memory_order /*unused*/, std::memory_order /*unused*/)
170 {
171 if(_v == expected)
172 {
173 _v = v;
174 return true;
175 }
176 return false;
177 }
178 };
179
180#ifdef BOOST_OUTCOME_FOUND_COROUTINE_HEADER
181 template <class Awaitable, bool suspend_initial, bool use_atomic, bool is_void> struct outcome_promise_type
182 {
183 using container_type = typename Awaitable::container_type;
184 using result_set_type = std::conditional_t<use_atomic, std::atomic<bool>, fake_atomic<bool>>;
185 union
186 {
187 BOOST_OUTCOME_V2_NAMESPACE::detail::empty_type _default{};
188 container_type result;
189 };
190 result_set_type result_set{false}, pending_first_resumption{is_initially_suspended};
191 coroutine_handle<> continuation;
192
193 static constexpr bool is_initially_suspended = suspend_initial;
194 static constexpr bool is_using_atomics = use_atomic;
195
196 outcome_promise_type() noexcept { BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(this << " promise constructed"); }
197 outcome_promise_type(const outcome_promise_type &) = delete;
198 outcome_promise_type(outcome_promise_type &&) = delete;
199 outcome_promise_type &operator=(const outcome_promise_type &) = delete;
200 outcome_promise_type &operator=(outcome_promise_type &&) = delete;
201 ~outcome_promise_type()
202 {
203 BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(this << " promise destructs");
204 if(result_set.load(std::memory_order_acquire))
205 {
206 result.~container_type(); // could throw
207 }
208 }
209 auto get_return_object()
210 {
211 BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(this << " promise returns awaitable");
212 return Awaitable{*this}; // could throw bad_alloc
213 }
214 void return_value(container_type &&value)
215 {
216 BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(this << " promise returns value");
217 BOOST_OUTCOME_ASSERT(!result_set.load(std::memory_order_acquire));
218 if(result_set.load(std::memory_order_acquire))
219 {
220 result.~container_type(); // could throw
221 }
222 new(&result) container_type(static_cast<container_type &&>(value)); // could throw
223 result_set.store(true, std::memory_order_release);
224 }
225 void return_value(const container_type &value)
226 {
227 BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(this << " promise returns value");
228 BOOST_OUTCOME_ASSERT(!result_set.load(std::memory_order_acquire));
229 if(result_set.load(std::memory_order_acquire))
230 {
231 result.~container_type(); // could throw
232 }
233 new(&result) container_type(value); // could throw
234 result_set.store(true, std::memory_order_release);
235 }
236 void unhandled_exception()
237 {
238 BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(this << " promise unhandled exception");
239 BOOST_OUTCOME_ASSERT(!result_set.load(std::memory_order_acquire));
240 if(result_set.load(std::memory_order_acquire))
241 {
242 result.~container_type();
243 }
244#ifndef BOOST_NO_EXCEPTIONS
245 auto e = std::current_exception();
246 auto ec = detail::error_from_exception(static_cast<decltype(e) &&>(e), {});
247 // Try to set error code first
248 if(!detail::error_is_set(ec) || !detail::try_set_error(static_cast<decltype(ec) &&>(ec), &result))
249 {
250 detail::set_or_rethrow(e, &result); // could throw
251 }
252#else
253 std::terminate();
254#endif
255 result_set.store(true, std::memory_order_release);
256 }
257 auto initial_suspend() noexcept
258 {
259 BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(this << " promise initial suspend = " << suspend_initial);
260 struct awaiter
261 {
262 constexpr bool await_ready() noexcept { return !suspend_initial; }
263 void await_resume() noexcept {}
264 void await_suspend(coroutine_handle<> /*unused*/) noexcept {}
265 };
266 return awaiter{};
267 }
268 auto final_suspend() noexcept
269 {
270 struct awaiter
271 {
272 // If we don't force a final suspend, promise will get deleted before awaitable
273 // TODO: Implement detachable awaitables
274 constexpr bool await_ready() noexcept { return false; }
275 void await_resume() noexcept {}
276#if BOOST_OUTCOME_HAVE_NOOP_COROUTINE
277 coroutine_handle<> await_suspend(coroutine_handle<outcome_promise_type> self) noexcept
278 {
279 if(self.promise().continuation)
280 {
281 BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(&self.promise() << " promise final suspend will resume coroutine " << self.promise().continuation.address());
282 }
283 else
284 {
285 BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(&self.promise() << " promise final suspend will exit");
286 }
287 return self.promise().continuation ? self.promise().continuation : noop_coroutine();
288 }
289#else
290 void await_suspend(coroutine_handle<outcome_promise_type> self)
291 {
292 if(self.promise().continuation)
293 {
294 BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(&self.promise() << " promise final suspend will resume coroutine " << self.promise().continuation.address());
295 return self.promise().continuation.resume();
296 }
297 else
298 {
299 BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(&self.promise() << " promise final suspend will exit");
300 }
301 }
302#endif
303 };
304 return awaiter{};
305 }
306 };
307 template <class Awaitable, bool suspend_initial, bool use_atomic> struct outcome_promise_type<Awaitable, suspend_initial, use_atomic, true>
308 {
309 using container_type = void;
310 using result_set_type = std::conditional_t<use_atomic, std::atomic<bool>, fake_atomic<bool>>;
311 result_set_type result_set{false}, pending_first_resumption{is_initially_suspended};
312 coroutine_handle<> continuation;
313
314 static constexpr bool is_initially_suspended = suspend_initial;
315 static constexpr bool is_using_atomics = use_atomic;
316
317 outcome_promise_type() { BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(this << " promise constructed"); }
318 outcome_promise_type(const outcome_promise_type &) = delete;
319 outcome_promise_type(outcome_promise_type &&) = delete;
320 outcome_promise_type &operator=(const outcome_promise_type &) = delete;
321 outcome_promise_type &operator=(outcome_promise_type &&) = delete;
322 ~outcome_promise_type() = default;
323 auto get_return_object()
324 {
325 BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(this << " promise returns awaitable");
326 return Awaitable{*this}; // could throw bad_alloc
327 }
328 void return_void() noexcept
329 {
330 BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(this << " promise returns void");
331 BOOST_OUTCOME_ASSERT(!result_set.load(std::memory_order_acquire));
332 result_set.store(true, std::memory_order_release);
333 }
334 void unhandled_exception()
335 {
336 BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(this << " promise unhandled exception");
337 BOOST_OUTCOME_ASSERT(!result_set.load(std::memory_order_acquire));
338 std::rethrow_exception(std::current_exception()); // throws
339 }
340 auto initial_suspend() noexcept
341 {
342 BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(this << " promise initial suspend = " << suspend_initial);
343 struct awaiter
344 {
345 constexpr bool await_ready() noexcept { return !suspend_initial; }
346 void await_resume() noexcept {}
347 void await_suspend(coroutine_handle<> /*unused*/) noexcept {}
348 };
349 return awaiter{};
350 }
351 auto final_suspend() noexcept
352 {
353 struct awaiter
354 {
355 // If we don't force a final suspend, promise will get deleted before awaitable
356 // TODO: Implement detachable awaitables
357 constexpr bool await_ready() noexcept { return false; }
358 void await_resume() noexcept {}
359#if BOOST_OUTCOME_HAVE_NOOP_COROUTINE
360 coroutine_handle<> await_suspend(coroutine_handle<outcome_promise_type> self) noexcept
361 {
362 if(self.promise().continuation)
363 {
364 BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(&self.promise() << " promise final suspend will resume coroutine " << self.promise().continuation.address());
365 }
366 else
367 {
368 BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(&self.promise() << " promise final suspend will exit");
369 }
370 return self.promise().continuation ? self.promise().continuation : noop_coroutine();
371 }
372#else
373 void await_suspend(coroutine_handle<outcome_promise_type> self)
374 {
375 if(self.promise().continuation)
376 {
377 BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(&self.promise() << " promise final suspend will resume coroutine " << self.promise().continuation.address());
378 return self.promise().continuation.resume();
379 }
380 else
381 {
382 BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(&self.promise() << " promise final suspend will exit");
383 }
384 }
385#endif
386 };
387 return awaiter{};
388 }
389 };
390 template <class Awaitable, bool suspend_initial, bool use_atomic>
391 constexpr inline auto move_result_from_promise_if_not_void(outcome_promise_type<Awaitable, suspend_initial, use_atomic, false> &p)
392 {
393 return static_cast<typename Awaitable::container_type &&>(p.result);
394 }
395 template <class Awaitable, bool suspend_initial, bool use_atomic>
396 constexpr inline void move_result_from_promise_if_not_void(outcome_promise_type<Awaitable, suspend_initial, use_atomic, true> & /*unused*/)
397 {
398 }
399
400 template <class Cont, class Executor, bool suspend_initial, bool use_atomic> struct BOOST_OUTCOME_NODISCARD awaitable
401 {
402 using container_type = Cont;
403 using value_type = Cont;
404 using executor_type = Executor;
405 using promise_type = outcome_promise_type<awaitable, suspend_initial, use_atomic, std::is_void<container_type>::value>;
406 coroutine_handle<promise_type> _h;
407
408 awaitable(awaitable &&o) noexcept
409 : _h(static_cast<coroutine_handle<promise_type> &&>(o._h))
410 {
411 o._h = nullptr;
412 }
413 awaitable(const awaitable &o) = delete;
414 awaitable &operator=(awaitable &&) = delete; // as per P1056
415 awaitable &operator=(const awaitable &) = delete;
416 ~awaitable()
417 {
418 BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(&_h.promise() << " awaitable destructs");
419 if(_h)
420 {
421 _h.destroy();
422 }
423 }
424 explicit awaitable(promise_type &p) // could throw
425 : _h(coroutine_handle<promise_type>::from_promise(p))
426 {
427 BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(&_h.promise() << " awaitable constructs for coroutine " << _h.address()
428 << " shall resume on first suspend = " << promise_type::is_initially_suspended);
429 }
430 bool valid() const noexcept { return _h != nullptr; }
431 bool await_ready() noexcept
432 {
433 BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(&_h.promise() << " await_ready = " << _h.promise().result_set.load(std::memory_order_acquire));
434 return _h.promise().result_set.load(std::memory_order_acquire);
435 }
436 container_type await_resume()
437 {
438 BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(&_h.promise() << " await_resume");
439 BOOST_OUTCOME_ASSERT(_h.promise().result_set.load(std::memory_order_acquire));
440 if(!_h.promise().result_set.load(std::memory_order_acquire))
441 {
442 std::terminate();
443 }
444 return detail::move_result_from_promise_if_not_void(_h.promise());
445 }
446#if BOOST_OUTCOME_HAVE_NOOP_COROUTINE
447 coroutine_handle<> await_suspend(coroutine_handle<> cont) noexcept
448 {
449 BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(&_h.promise() << " await_suspend suspends coroutine " << cont.address());
450 auto &p = _h.promise();
451 p.continuation = cont;
452 bool expected = true;
453 if(p.pending_first_resumption.compare_exchange_strong(expected, false, std::memory_order_acq_rel, std::memory_order_relaxed))
454 {
455 BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(&_h.promise()
456 << " await_suspend does one time first resumption of initially suspended coroutine " << _h.address());
457 return _h;
458 }
459 return noop_coroutine();
460 }
461#else
462 void await_suspend(coroutine_handle<> cont)
463 {
464 BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(&_h.promise() << " await_suspend suspends coroutine " << cont.address());
465 auto &p = _h.promise();
466 p.continuation = cont;
467 bool expected = true;
468 if(p.pending_first_resumption.compare_exchange_strong(expected, false, std::memory_order_acq_rel, std::memory_order_relaxed))
469 {
470 BOOST_OUTCOME_V2_AWAITABLES_DEBUG_PRINTER(&_h.promise()
471 << " await_suspend does one time first resumption of initially suspended coroutine " << _h.address());
472 _h.resume();
473 }
474 }
475#endif
476 };
477
478 template <class ContType, class Executor, bool suspend_initial, bool use_atomic> struct generator
479 {
480 using container_type = ContType;
481 using value_type = ContType;
482 using executor_type = Executor;
483 class promise_type
484 {
485 friend struct generator;
486 using result_set_type = std::conditional_t<use_atomic, std::atomic<int8_t>, fake_atomic<int8_t>>;
487 union
488 {
489 BOOST_OUTCOME_V2_NAMESPACE::detail::empty_type _default{};
490 container_type result;
491 };
492 result_set_type result_set{0};
493 coroutine_handle<> continuation;
494
495 public:
496 promise_type() {}
497 promise_type(const promise_type &) = delete;
498 promise_type(promise_type &&) = delete;
499 promise_type &operator=(const promise_type &) = delete;
500 promise_type &operator=(promise_type &&) = delete;
501 ~promise_type()
502 {
503 if(result_set.load(std::memory_order_acquire) == 1)
504 {
505 result.~container_type(); // could throw
506 }
507 }
508
509 auto get_return_object()
510 {
511 return generator{*this}; // could throw bad_alloc
512 }
513 void return_void() noexcept
514 {
515 BOOST_OUTCOME_ASSERT(result_set.load(std::memory_order_acquire) >= 0);
516 if(result_set.load(std::memory_order_acquire) == 1)
517 {
518 result.~container_type(); // could throw
519 }
520 result_set.store(-1, std::memory_order_release);
521 }
522 suspend_always yield_value(container_type &&value)
523 {
524 BOOST_OUTCOME_ASSERT(result_set.load(std::memory_order_acquire) >= 0);
525 if(result_set.load(std::memory_order_acquire) == 1)
526 {
527 result.~container_type(); // could throw
528 }
529 new(&result) container_type(static_cast<container_type &&>(value)); // could throw
530 result_set.store(1, std::memory_order_release);
531 return {};
532 }
533 suspend_always yield_value(const container_type &value)
534 {
535 BOOST_OUTCOME_ASSERT(result_set.load(std::memory_order_acquire) >= 0);
536 if(result_set.load(std::memory_order_acquire) == 1)
537 {
538 result.~container_type(); // could throw
539 }
540 new(&result) container_type(value); // could throw
541 result_set.store(1, std::memory_order_release);
542 return {};
543 }
544 void unhandled_exception()
545 {
546 BOOST_OUTCOME_ASSERT(result_set.load(std::memory_order_acquire) >= 0);
547 if(result_set.load(std::memory_order_acquire) == 1)
548 {
549 result.~container_type();
550 }
551#ifndef BOOST_NO_EXCEPTIONS
552 auto e = std::current_exception();
553 auto ec = detail::error_from_exception(static_cast<decltype(e) &&>(e), {});
554 // Try to set error code first
555 if(!detail::error_is_set(ec) || !detail::try_set_error(static_cast<decltype(ec) &&>(ec), &result))
556 {
557 detail::set_or_rethrow(e, &result); // could throw
558 }
559#else
560 std::terminate();
561#endif
562 result_set.store(1, std::memory_order_release);
563 }
564 auto initial_suspend() noexcept
565 {
566 struct awaiter
567 {
568 bool await_ready() noexcept { return !suspend_initial; }
569 void await_resume() noexcept {}
570 void await_suspend(coroutine_handle<> /*unused*/) noexcept {}
571 };
572 return awaiter{};
573 }
574 auto final_suspend() noexcept
575 {
576 struct awaiter
577 {
578 bool await_ready() noexcept { return false; }
579 void await_resume() noexcept {}
580#if BOOST_OUTCOME_HAVE_NOOP_COROUTINE
581 coroutine_handle<> await_suspend(coroutine_handle<promise_type> self) noexcept
582 {
583 return self.promise().continuation ? self.promise().continuation : noop_coroutine();
584 }
585#else
586 void await_suspend(coroutine_handle<promise_type> self)
587 {
588 if(self.promise().continuation)
589 {
590 return self.promise().continuation.resume();
591 }
592 }
593#endif
594 };
595 return awaiter{};
596 }
597 };
598 coroutine_handle<promise_type> _h;
599
600 generator(generator &&o) noexcept
601 : _h(static_cast<coroutine_handle<promise_type> &&>(o._h))
602 {
603 o._h = nullptr;
604 }
605 generator(const generator &o) = delete;
606 generator &operator=(generator &&) = delete; // as per P1056
607 generator &operator=(const generator &) = delete;
608 ~generator()
609 {
610 if(_h)
611 {
612 _h.destroy();
613 }
614 }
615 explicit generator(promise_type &p) // could throw
616 : _h(coroutine_handle<promise_type>::from_promise(p))
617 {
618 }
619 explicit operator bool() const // could throw
620 {
621 return valid();
622 }
623 bool valid() const // could throw
624 {
625 auto &p = _h.promise();
626 if(p.result_set.load(std::memory_order_acquire) == 0)
627 {
628 const_cast<generator *>(this)->_h();
629 }
630 return p.result_set.load(std::memory_order_acquire) >= 0;
631 }
632 container_type operator()() // could throw
633 {
634 auto &p = _h.promise();
635 if(p.result_set.load(std::memory_order_acquire) == 0)
636 {
637 _h();
638 }
639 BOOST_OUTCOME_ASSERT(p.result_set.load(std::memory_order_acquire) >= 0);
640 if(p.result_set.load(std::memory_order_acquire) < 0)
641 {
642 std::terminate();
643 }
644 container_type ret(static_cast<container_type &&>(p.result));
645 p.result.~container_type(); // could throw
646 p.result_set.store(0, std::memory_order_release);
647 return ret;
648 }
649#if BOOST_OUTCOME_HAVE_NOOP_COROUTINE
650 coroutine_handle<> await_suspend(coroutine_handle<> cont) noexcept
651 {
652 _h.promise().continuation = cont;
653 return _h;
654 }
655#else
656 void await_suspend(coroutine_handle<> cont)
657 {
658 _h.promise().continuation = cont;
659 _h.resume();
660 }
661#endif
662 };
663#endif
664 } // namespace detail
665
666} // namespace awaitables
667
668BOOST_OUTCOME_V2_NAMESPACE_END
669
670#endif
671
672#ifdef BOOST_OUTCOME_FOUND_COROUTINE_HEADER
673BOOST_OUTCOME_COROUTINE_SUPPORT_NAMESPACE_EXPORT_BEGIN
674/*! AWAITING HUGO JSON CONVERSION TOOL
675SIGNATURE NOT RECOGNISED
676*/
677template <class T, class Executor = void> using eager = BOOST_OUTCOME_V2_NAMESPACE::awaitables::detail::awaitable<T, Executor, false, false>;
678
679/*! AWAITING HUGO JSON CONVERSION TOOL
680SIGNATURE NOT RECOGNISED
681*/
682template <class T, class Executor = void> using atomic_eager = BOOST_OUTCOME_V2_NAMESPACE::awaitables::detail::awaitable<T, Executor, false, true>;
683
684/*! AWAITING HUGO JSON CONVERSION TOOL
685SIGNATURE NOT RECOGNISED
686*/
687template <class T, class Executor = void> using lazy = BOOST_OUTCOME_V2_NAMESPACE::awaitables::detail::awaitable<T, Executor, true, false>;
688
689/*! AWAITING HUGO JSON CONVERSION TOOL
690SIGNATURE NOT RECOGNISED
691*/
692template <class T, class Executor = void> using atomic_lazy = BOOST_OUTCOME_V2_NAMESPACE::awaitables::detail::awaitable<T, Executor, true, true>;
693
694/*! AWAITING HUGO JSON CONVERSION TOOL
695SIGNATURE NOT RECOGNISED
696*/
697template <class T, class Executor = void> using generator = BOOST_OUTCOME_V2_NAMESPACE::awaitables::detail::generator<T, Executor, true, false>;
698
699BOOST_OUTCOME_COROUTINE_SUPPORT_NAMESPACE_END
700#endif
701

source code of boost/libs/outcome/include/boost/outcome/detail/coroutine_support.ipp