coroutine_support.ipp 26 KB

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