generator.hpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635
  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. #ifndef BOOST_COBALT_DETAIL_GENERATOR_HPP
  8. #define BOOST_COBALT_DETAIL_GENERATOR_HPP
  9. #include <boost/cobalt/concepts.hpp>
  10. #include <boost/cobalt/result.hpp>
  11. #include <boost/cobalt/detail/exception.hpp>
  12. #include <boost/cobalt/detail/forward_cancellation.hpp>
  13. #include <boost/cobalt/detail/this_thread.hpp>
  14. #include <boost/cobalt/unique_handle.hpp>
  15. #include <boost/cobalt/detail/wrapper.hpp>
  16. #include <boost/cobalt/op.hpp>
  17. #include <boost/cobalt/noop.hpp>
  18. #include <boost/asio/bind_allocator.hpp>
  19. #include <boost/core/exchange.hpp>
  20. #include <boost/variant2/variant.hpp>
  21. namespace boost::cobalt
  22. {
  23. template<typename Yield, typename Push>
  24. struct generator;
  25. namespace detail
  26. {
  27. template<typename Yield, typename Push>
  28. struct generator_yield_awaitable;
  29. template<typename Yield, typename Push>
  30. struct generator_receiver;
  31. template<typename Yield, typename Push>
  32. struct generator_receiver_base
  33. {
  34. std::optional<Push> pushed_value;
  35. auto get_awaitable(const Push & push) requires std::is_copy_constructible_v<Push>
  36. {
  37. using impl = generator_receiver<Yield, Push>;
  38. return typename impl::awaitable{static_cast<impl*>(this), &push};
  39. }
  40. auto get_awaitable( Push && push)
  41. {
  42. using impl = generator_receiver<Yield, Push>;
  43. return typename impl::awaitable{static_cast<impl*>(this), &push};
  44. }
  45. };
  46. template<typename Yield>
  47. struct generator_receiver_base<Yield, void>
  48. {
  49. bool pushed_value{false};
  50. auto get_awaitable()
  51. {
  52. using impl = generator_receiver<Yield, void>;
  53. return typename impl::awaitable{static_cast<impl*>(this), static_cast<void*>(nullptr)};
  54. }
  55. };
  56. template<typename Yield, typename Push>
  57. struct generator_promise;
  58. template<typename Yield, typename Push>
  59. struct generator_receiver : generator_receiver_base<Yield, Push>
  60. {
  61. std::exception_ptr exception;
  62. std::optional<Yield> result, result_buffer;
  63. Yield get_result()
  64. {
  65. if (result_buffer)
  66. {
  67. auto res = *std::exchange(result, std::nullopt);
  68. if (result_buffer)
  69. result.emplace(*std::exchange(result_buffer, std::nullopt));
  70. return res;
  71. }
  72. else
  73. return *std::exchange(result, std::nullopt);
  74. }
  75. bool done = false;
  76. unique_handle<void> awaited_from{nullptr};
  77. unique_handle<generator_promise<Yield, Push>> yield_from{nullptr};
  78. bool lazy = false;
  79. bool ready() { return exception || result || done; }
  80. generator_receiver(noop<Yield> n) : result(std::move(n.value)), done(true) {}
  81. generator_receiver() = default;
  82. generator_receiver(generator_receiver && lhs)
  83. : generator_receiver_base<Yield, Push>{std::move(lhs.pushed_value)},
  84. exception(std::move(lhs.exception)),
  85. result(std::move(lhs.result)),
  86. result_buffer(std::move(lhs.result_buffer)), done(lhs.done),
  87. awaited_from(std::move(lhs.awaited_from)), yield_from{std::move(lhs.yield_from)},
  88. lazy(lhs.lazy), reference(lhs.reference), cancel_signal(lhs.cancel_signal)
  89. {
  90. if (!lhs.done && !lhs.exception)
  91. {
  92. *reference = this;
  93. lhs.exception = moved_from_exception();
  94. }
  95. lhs.done = true;
  96. }
  97. ~generator_receiver()
  98. {
  99. if (!done && *reference == this)
  100. *reference = nullptr;
  101. }
  102. generator_receiver(generator_receiver * &reference, asio::cancellation_signal & cancel_signal)
  103. : reference(&reference), cancel_signal(&cancel_signal)
  104. {
  105. reference = this;
  106. }
  107. generator_receiver& operator=(generator_receiver && lhs) noexcept
  108. {
  109. if (*reference == this)
  110. {
  111. *reference = nullptr;
  112. }
  113. generator_receiver_base<Yield, Push>::operator=(std::move(lhs));
  114. exception = std::move(lhs.exception);
  115. done = lhs.done;
  116. result = std::move(lhs.result);
  117. result_buffer = std::move(lhs.result_buffer);
  118. awaited_from = std::move(lhs.awaited_from);
  119. yield_from = std::move(lhs.yield_from);
  120. lazy = lhs.lazy;
  121. reference = lhs.reference;
  122. cancel_signal = lhs.cancel_signal;
  123. if (!lhs.done && !lhs.exception)
  124. {
  125. *reference = this;
  126. lhs.exception = moved_from_exception();
  127. }
  128. lhs.done = true;
  129. return *this;
  130. }
  131. generator_receiver **reference;
  132. asio::cancellation_signal * cancel_signal;
  133. using yield_awaitable = generator_yield_awaitable<Yield, Push>;
  134. yield_awaitable get_yield_awaitable(generator_promise<Yield, Push> * pro) {return {pro}; }
  135. static yield_awaitable terminator() {return {nullptr}; }
  136. template<typename T>
  137. void yield_value(T && t)
  138. {
  139. if (!result)
  140. result.emplace(std::forward<T>(t));
  141. else
  142. {
  143. BOOST_ASSERT(!result_buffer);
  144. result_buffer.emplace(std::forward<T>(t));
  145. }
  146. }
  147. struct awaitable
  148. {
  149. generator_receiver *self;
  150. std::exception_ptr ex;
  151. asio::cancellation_slot cl;
  152. variant2::variant<variant2::monostate, Push *, const Push *> to_push;
  153. awaitable(generator_receiver * self, Push * to_push) : self(self), to_push(to_push)
  154. {
  155. }
  156. awaitable(generator_receiver * self, const Push * to_push)
  157. : self(self), to_push(to_push)
  158. {
  159. }
  160. awaitable(const awaitable & aw) noexcept : self(aw.self), to_push(aw.to_push)
  161. {
  162. }
  163. bool await_ready() const
  164. {
  165. BOOST_ASSERT(!ex);
  166. return self->ready();
  167. }
  168. template<typename Promise>
  169. std::coroutine_handle<void> await_suspend(std::coroutine_handle<Promise> h)
  170. {
  171. if (self->done) // ok, so we're actually done already, so noop
  172. return std::noop_coroutine();
  173. if (!ex && self->awaited_from != nullptr) // generator already being awaited, that's an error!
  174. ex = already_awaited();
  175. if (ex)
  176. return h;
  177. if constexpr (requires (Promise p) {p.get_cancellation_slot();})
  178. if ((cl = h.promise().get_cancellation_slot()).is_connected())
  179. cl.emplace<forward_cancellation>(*self->cancel_signal);
  180. self->awaited_from.reset(h.address());
  181. std::coroutine_handle<void> res = std::noop_coroutine();
  182. if (self->yield_from != nullptr)
  183. res = self->yield_from.release();
  184. if ((to_push.index() > 0) && !self->pushed_value && self->lazy)
  185. {
  186. if constexpr (std::is_void_v<Push>)
  187. self->pushed_value = true;
  188. else
  189. {
  190. if (to_push.index() == 1)
  191. self->pushed_value.emplace(std::move(*variant2::get<1>(to_push)));
  192. else
  193. {
  194. if constexpr (std::is_copy_constructible_v<Push>)
  195. self->pushed_value.emplace(std::move(*variant2::get<2>(to_push)));
  196. else
  197. {
  198. BOOST_ASSERT(!"push value is not movable");
  199. }
  200. }
  201. }
  202. to_push = variant2::monostate{};
  203. }
  204. return std::coroutine_handle<void>::from_address(res.address());
  205. }
  206. Yield await_resume(const boost::source_location & loc = BOOST_CURRENT_LOCATION)
  207. {
  208. return await_resume(as_result_tag{}).value(loc);
  209. }
  210. std::tuple<std::exception_ptr, Yield> await_resume(
  211. const as_tuple_tag &)
  212. {
  213. auto res = await_resume(as_result_tag{});
  214. if (res.has_error())
  215. return {res.error(), Yield{}};
  216. else
  217. return {nullptr, res.value()};
  218. }
  219. system::result<Yield, std::exception_ptr> await_resume(const as_result_tag& )
  220. {
  221. if (cl.is_connected())
  222. cl.clear();
  223. if (ex)
  224. return {system::in_place_error, ex};
  225. if (self->exception)
  226. return {system::in_place_error, std::exchange(self->exception, nullptr)};
  227. if (!self->result) // missing co_return this is accepted behaviour, if the compiler agrees
  228. return {system::in_place_error, std::make_exception_ptr(std::runtime_error("cobalt::generator returned void"))};
  229. if (to_push.index() > 0)
  230. {
  231. BOOST_ASSERT(!self->pushed_value);
  232. if constexpr (std::is_void_v<Push>)
  233. self->pushed_value = true;
  234. else
  235. {
  236. if (to_push.index() == 1)
  237. self->pushed_value.emplace(std::move(*variant2::get<1>(to_push)));
  238. else
  239. {
  240. if constexpr (std::is_copy_constructible_v<Push>)
  241. self->pushed_value.emplace(std::move(*variant2::get<2>(to_push)));
  242. else
  243. {
  244. BOOST_ASSERT(!"push value is not movable");
  245. }
  246. }
  247. }
  248. to_push = variant2::monostate{};
  249. }
  250. // now we also want to resume the coroutine, so it starts work
  251. if (self->yield_from != nullptr && !self->lazy)
  252. {
  253. auto exec = self->yield_from->get_executor();
  254. auto alloc = asio::get_associated_allocator(self->yield_from);
  255. asio::post(
  256. std::move(exec),
  257. asio::bind_allocator(
  258. alloc,
  259. [y = std::exchange(self->yield_from, nullptr)]() mutable
  260. {
  261. if (y->receiver) // make sure we only resume eagerly when attached to a generator object
  262. std::move(y)();
  263. }));
  264. }
  265. return {system::in_place_value, self->get_result()};
  266. }
  267. void interrupt_await() &
  268. {
  269. if (!self)
  270. return ;
  271. ex = detached_exception();
  272. if (self->awaited_from)
  273. self->awaited_from.release().resume();
  274. }
  275. };
  276. void interrupt_await() &
  277. {
  278. exception = detached_exception();
  279. awaited_from.release().resume();
  280. }
  281. void rethrow_if()
  282. {
  283. if (exception)
  284. std::rethrow_exception(exception);
  285. }
  286. };
  287. template<typename Yield, typename Push>
  288. struct generator_promise
  289. : promise_memory_resource_base,
  290. promise_cancellation_base<asio::cancellation_slot, asio::enable_total_cancellation>,
  291. promise_throw_if_cancelled_base,
  292. enable_awaitables<generator_promise<Yield, Push>>,
  293. enable_await_allocator<generator_promise<Yield, Push>>,
  294. enable_await_executor< generator_promise<Yield, Push>>,
  295. enable_await_deferred
  296. {
  297. using promise_cancellation_base<asio::cancellation_slot, asio::enable_total_cancellation>::await_transform;
  298. using promise_throw_if_cancelled_base::await_transform;
  299. using enable_awaitables<generator_promise<Yield, Push>>::await_transform;
  300. using enable_await_allocator<generator_promise<Yield, Push>>::await_transform;
  301. using enable_await_executor<generator_promise<Yield, Push>>::await_transform;
  302. using enable_await_deferred::await_transform;
  303. [[nodiscard]] generator<Yield, Push> get_return_object()
  304. {
  305. return generator<Yield, Push>{this};
  306. }
  307. mutable asio::cancellation_signal signal;
  308. using executor_type = executor;
  309. executor_type exec;
  310. const executor_type & get_executor() const {return exec;}
  311. template<typename ... Args>
  312. generator_promise(Args & ...args)
  313. :
  314. #if !defined(BOOST_COBALT_NO_PMR)
  315. promise_memory_resource_base(detail::get_memory_resource_from_args(args...)),
  316. #endif
  317. exec{detail::get_executor_from_args(args...)}
  318. {
  319. this->reset_cancellation_source(signal.slot());
  320. }
  321. std::suspend_never initial_suspend() noexcept {return {};}
  322. struct final_awaitable
  323. {
  324. generator_promise * generator;
  325. bool await_ready() const noexcept
  326. {
  327. return generator->receiver && generator->receiver->awaited_from.get() == nullptr;
  328. }
  329. auto await_suspend(std::coroutine_handle<generator_promise> h) noexcept
  330. {
  331. std::coroutine_handle<void> res = std::noop_coroutine();
  332. if (generator->receiver && generator->receiver->awaited_from.get() != nullptr)
  333. res = generator->receiver->awaited_from.release();
  334. if (generator->receiver)
  335. generator->receiver->done = true;
  336. if (auto & rec = h.promise().receiver; rec != nullptr)
  337. {
  338. if (!rec->done && !rec->exception)
  339. rec->exception = detail::completed_unexpected();
  340. rec->done = true;
  341. rec->awaited_from.reset(nullptr);
  342. rec = nullptr;
  343. }
  344. detail::self_destroy(h);
  345. return res;
  346. }
  347. void await_resume() noexcept
  348. {
  349. if (generator->receiver)
  350. generator->receiver->done = true;
  351. }
  352. };
  353. auto final_suspend() noexcept
  354. {
  355. return final_awaitable{this};
  356. }
  357. void unhandled_exception()
  358. {
  359. if (this->receiver)
  360. this->receiver->exception = std::current_exception();
  361. else
  362. throw ;
  363. }
  364. void return_value(const Yield & res) requires std::is_copy_constructible_v<Yield>
  365. {
  366. if (this->receiver)
  367. this->receiver->yield_value(res);
  368. }
  369. void return_value(Yield && res)
  370. {
  371. if (this->receiver)
  372. this->receiver->yield_value(std::move(res));
  373. }
  374. generator_receiver<Yield, Push>* receiver{nullptr};
  375. auto await_transform(this_coro::initial_t val)
  376. {
  377. if(receiver)
  378. {
  379. receiver->lazy = true;
  380. return receiver->get_yield_awaitable(this);
  381. }
  382. else
  383. return generator_receiver<Yield, Push>::terminator();
  384. }
  385. template<typename Yield_>
  386. auto yield_value(Yield_ && ret)
  387. {
  388. if(receiver)
  389. {
  390. // if this is lazy, there might still be a value in there.
  391. receiver->yield_value(std::forward<Yield_>(ret));
  392. return receiver->get_yield_awaitable(this);
  393. }
  394. else
  395. return generator_receiver<Yield, Push>::terminator();
  396. }
  397. void interrupt_await() &
  398. {
  399. if (this->receiver)
  400. {
  401. this->receiver->exception = detached_exception();
  402. std::coroutine_handle<void>::from_address(this->receiver->awaited_from.release()).resume();
  403. }
  404. }
  405. ~generator_promise()
  406. {
  407. if (this->receiver)
  408. {
  409. if (!this->receiver->done && !this->receiver->exception)
  410. this->receiver->exception = detail::completed_unexpected();
  411. this->receiver->done = true;
  412. this->receiver->awaited_from.reset(nullptr);
  413. }
  414. }
  415. };
  416. template<typename Yield, typename Push>
  417. struct generator_yield_awaitable
  418. {
  419. generator_promise<Yield, Push> *self;
  420. constexpr bool await_ready() const
  421. {
  422. return self && self->receiver && self->receiver->pushed_value && !self->receiver->result;
  423. }
  424. std::coroutine_handle<void> await_suspend(
  425. std::coroutine_handle<generator_promise<Yield, Push>> h
  426. #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
  427. , const boost::source_location & loc = BOOST_CURRENT_LOCATION
  428. #endif
  429. )
  430. {
  431. if (self == nullptr) // we're a terminator, kill it
  432. {
  433. if (auto & rec = h.promise().receiver; rec != nullptr)
  434. {
  435. if (!rec->done && !rec->exception)
  436. rec->exception = detail::completed_unexpected();
  437. rec->done = true;
  438. rec->awaited_from.reset(nullptr);
  439. rec = nullptr;
  440. }
  441. detail::self_destroy(h);
  442. return std::noop_coroutine();
  443. }
  444. std::coroutine_handle<void> res = std::noop_coroutine();
  445. if (self->receiver->awaited_from.get() != nullptr)
  446. res = self->receiver->awaited_from.release();
  447. #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
  448. self->receiver->yield_from.reset(&h.promise(), loc);
  449. #else
  450. self->receiver->yield_from.reset(&h.promise());
  451. #endif
  452. return res;
  453. }
  454. Push await_resume()
  455. {
  456. BOOST_ASSERT(self->receiver);
  457. BOOST_ASSERT(self->receiver->pushed_value);
  458. return *std::exchange(self->receiver->pushed_value, std::nullopt);
  459. }
  460. };
  461. template<typename Yield>
  462. struct generator_yield_awaitable<Yield, void>
  463. {
  464. generator_promise<Yield, void> *self;
  465. constexpr bool await_ready() { return self && self->receiver && self->receiver->pushed_value; }
  466. std::coroutine_handle<> await_suspend(
  467. std::coroutine_handle<generator_promise<Yield, void>> h
  468. #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
  469. , const boost::source_location & loc = BOOST_CURRENT_LOCATION
  470. #endif
  471. )
  472. {
  473. if (self == nullptr) // we're a terminator, kill it
  474. {
  475. if (auto & rec = h.promise().receiver; rec != nullptr)
  476. {
  477. if (!rec->done && !rec->exception)
  478. rec->exception = detail::completed_unexpected();
  479. rec->done = true;
  480. rec->awaited_from.reset(nullptr);
  481. rec = nullptr;
  482. }
  483. detail::self_destroy(h);
  484. return std::noop_coroutine();
  485. }
  486. std::coroutine_handle<void> res = std::noop_coroutine();
  487. BOOST_ASSERT(self);
  488. if (self->receiver->awaited_from.get() != nullptr)
  489. res = self->receiver->awaited_from.release();
  490. #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
  491. self->receiver->yield_from.reset(&h.promise(), loc);
  492. #else
  493. self->receiver->yield_from.reset(&h.promise());
  494. #endif
  495. return res;
  496. }
  497. void await_resume()
  498. {
  499. BOOST_ASSERT(self->receiver->pushed_value);
  500. self->receiver->pushed_value = false;
  501. }
  502. };
  503. template<typename Yield, typename Push>
  504. struct generator_base
  505. {
  506. auto operator()( Push && push)
  507. {
  508. return static_cast<generator<Yield, Push>*>(this)->receiver_.get_awaitable(std::move(push));
  509. }
  510. auto operator()(const Push & push) requires std::is_copy_constructible_v<Push>
  511. {
  512. return static_cast<generator<Yield, Push>*>(this)->receiver_.get_awaitable(push);
  513. }
  514. };
  515. template<typename Yield>
  516. struct generator_base<Yield, void>
  517. {
  518. auto operator co_await ()
  519. {
  520. return static_cast<generator<Yield, void>*>(this)->receiver_.get_awaitable();
  521. }
  522. };
  523. template<typename T>
  524. struct generator_with_awaitable
  525. {
  526. generator_base<T, void> &g;
  527. std::optional<typename detail::generator_receiver<T, void>::awaitable> awaitable;
  528. template<typename Promise>
  529. void await_suspend(std::coroutine_handle<Promise> h)
  530. {
  531. g.cancel();
  532. awaitable.emplace(g.operator co_await());
  533. return awaitable->await_suspend(h);
  534. }
  535. void await_resume() {}
  536. };
  537. }
  538. }
  539. #endif //BOOST_COBALT_DETAIL_GENERATOR_HPP