this_coro.hpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. // Copyright (c) 2022 Klemens D. Morgenstern
  2. //
  3. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  4. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  5. #ifndef BOOST_COBALT_THIS_CORO_HPP
  6. #define BOOST_COBALT_THIS_CORO_HPP
  7. #include <boost/cobalt/this_thread.hpp>
  8. #include <boost/cobalt/detail/this_thread.hpp>
  9. #include <boost/asio/associated_allocator.hpp>
  10. #include <boost/asio/associated_cancellation_slot.hpp>
  11. #include <boost/asio/associated_executor.hpp>
  12. #include <boost/asio/this_coro.hpp>
  13. #include <boost/asio/cancellation_state.hpp>
  14. #include <coroutine>
  15. #include <optional>
  16. #include <tuple>
  17. namespace boost::cobalt
  18. {
  19. namespace this_coro
  20. {
  21. /* tag::outline[]
  22. // Awaitable type that returns the executor of the current coroutine.
  23. struct executor_t {}
  24. constexpr executor_t executor;
  25. // Awaitable type that returns the cancellation state of the current coroutine.
  26. struct cancellation_state_t {};
  27. constexpr cancellation_state_t cancellation_state;
  28. // Reset the cancellation state with custom or default filters.
  29. constexpr __unspecified__ reset_cancellation_state();
  30. template<typename Filter>
  31. constexpr __unspecified__ reset_cancellation_state(
  32. Filter && filter);
  33. template<typename InFilter, typename OutFilter>
  34. constexpr __unspecified__ reset_cancellation_state(
  35. InFilter && in_filter,
  36. OutFilter && out_filter);
  37. // get & set the throw_if_cancelled setting.
  38. __unspecified__ throw_if_cancelled();
  39. __unspecified__ throw_if_cancelled(bool value);
  40. // Set the cancellation source in a detached.
  41. __unspecified__ reset_cancellation_source();
  42. __unspecified__ reset_cancellation_source(asio::cancellation_slot slot);
  43. end::outline[]
  44. */
  45. using namespace asio::this_coro;
  46. //tag::outline[]
  47. // get the allocator the promise
  48. struct allocator_t {};
  49. constexpr allocator_t allocator;
  50. // get the current cancellation state-type
  51. struct cancelled_t {};
  52. constexpr cancelled_t cancelled;
  53. // set the over-eager mode of a generator
  54. struct initial_t {};
  55. constexpr initial_t initial;
  56. //end::outline[]
  57. template<typename CancellationSlot = asio::cancellation_slot>
  58. struct reset_cancellation_source_t
  59. {
  60. CancellationSlot source;
  61. };
  62. template<typename CancellationSlot= asio::cancellation_slot>
  63. reset_cancellation_source_t<CancellationSlot> reset_cancellation_source(CancellationSlot slot = {})
  64. {
  65. return reset_cancellation_source_t<CancellationSlot>{std::move(slot)};
  66. }
  67. }
  68. template<typename CancellationSlot = asio::cancellation_slot,
  69. typename DefaultFilter = asio::enable_terminal_cancellation>
  70. struct promise_cancellation_base
  71. {
  72. using cancellation_slot_type = asio::cancellation_slot;
  73. cancellation_slot_type get_cancellation_slot() const {return state_.slot();}
  74. template<typename InitialFilter = asio::enable_terminal_cancellation>
  75. promise_cancellation_base(CancellationSlot slot = {}, InitialFilter filter = {})
  76. : source_(slot), state_{source_, filter} {}
  77. // This await transformation resets the associated cancellation state.
  78. auto await_transform(cobalt::this_coro::cancelled_t) noexcept
  79. {
  80. return cancelled_t_awaitable{state_.cancelled()};
  81. }
  82. // This await transformation resets the associated cancellation state.
  83. auto await_transform(asio::this_coro::cancellation_state_t) noexcept
  84. {
  85. return cancellation_state_t_awaitable{state_};
  86. }
  87. // This await transformation resets the associated cancellation state.
  88. auto await_transform(asio::this_coro::reset_cancellation_state_0_t) noexcept
  89. {
  90. return reset_cancellation_state_0_t_awaitable{state_, source_};
  91. }
  92. // This await transformation resets the associated cancellation state.
  93. template <typename Filter>
  94. auto await_transform(
  95. asio::this_coro::reset_cancellation_state_1_t<Filter> reset) noexcept
  96. {
  97. return reset_cancellation_state_1_t_awaitable<Filter>{state_, BOOST_ASIO_MOVE_CAST(Filter)(reset.filter), source_};
  98. }
  99. // This await transformation resets the associated cancellation state.
  100. template <typename InFilter, typename OutFilter>
  101. auto await_transform(
  102. asio::this_coro::reset_cancellation_state_2_t<InFilter, OutFilter> reset)
  103. noexcept
  104. {
  105. return reset_cancellation_state_2_t_awaitable<InFilter, OutFilter>{state_,
  106. BOOST_ASIO_MOVE_CAST(InFilter)(reset.in_filter),
  107. BOOST_ASIO_MOVE_CAST(OutFilter)(reset.out_filter),
  108. source_};
  109. }
  110. const asio::cancellation_state & cancellation_state() const {return state_;}
  111. asio::cancellation_state & cancellation_state() {return state_;}
  112. asio::cancellation_type cancelled() const
  113. {
  114. return state_.cancelled();
  115. }
  116. cancellation_slot_type get_cancellation_slot() {return state_.slot();}
  117. void reset_cancellation_source(CancellationSlot source = CancellationSlot())
  118. {
  119. source_ = source;
  120. state_ = asio::cancellation_state{source, DefaultFilter()};
  121. state_.clear();
  122. }
  123. CancellationSlot & source() {return source_;}
  124. const CancellationSlot & source() const {return source_;}
  125. private:
  126. CancellationSlot source_;
  127. asio::cancellation_state state_{source_, DefaultFilter() };
  128. struct cancelled_t_awaitable
  129. {
  130. asio::cancellation_type state;
  131. bool await_ready() const noexcept
  132. {
  133. return true;
  134. }
  135. void await_suspend(std::coroutine_handle<void>) noexcept
  136. {
  137. }
  138. auto await_resume() const
  139. {
  140. return state;
  141. }
  142. };
  143. struct cancellation_state_t_awaitable
  144. {
  145. asio::cancellation_state &state;
  146. bool await_ready() const noexcept
  147. {
  148. return true;
  149. }
  150. void await_suspend(std::coroutine_handle<void>) noexcept
  151. {
  152. }
  153. auto await_resume() const
  154. {
  155. return state;
  156. }
  157. };
  158. struct reset_cancellation_state_0_t_awaitable
  159. {
  160. asio::cancellation_state &state;
  161. CancellationSlot &source;
  162. bool await_ready() const noexcept
  163. {
  164. return true;
  165. }
  166. void await_suspend(std::coroutine_handle<void>) noexcept
  167. {
  168. }
  169. auto await_resume() const
  170. {
  171. state = asio::cancellation_state(source, DefaultFilter());
  172. }
  173. };
  174. template<typename Filter>
  175. struct reset_cancellation_state_1_t_awaitable
  176. {
  177. asio::cancellation_state & state;
  178. Filter filter_;
  179. CancellationSlot &source;
  180. bool await_ready() const noexcept
  181. {
  182. return true;
  183. }
  184. void await_suspend(std::coroutine_handle<void>) noexcept
  185. {
  186. }
  187. auto await_resume()
  188. {
  189. state = asio::cancellation_state(
  190. source,
  191. BOOST_ASIO_MOVE_CAST(Filter)(filter_));
  192. }
  193. };
  194. template<typename InFilter, typename OutFilter>
  195. struct reset_cancellation_state_2_t_awaitable
  196. {
  197. asio::cancellation_state & state;
  198. InFilter in_filter_;
  199. OutFilter out_filter_;
  200. CancellationSlot &source;
  201. bool await_ready() const noexcept
  202. {
  203. return true;
  204. }
  205. void await_suspend(std::coroutine_handle<void>) noexcept
  206. {
  207. }
  208. auto await_resume()
  209. {
  210. state = asio::cancellation_state(
  211. source,
  212. BOOST_ASIO_MOVE_CAST(InFilter)(in_filter_),
  213. BOOST_ASIO_MOVE_CAST(OutFilter)(out_filter_));
  214. }
  215. };
  216. };
  217. struct promise_throw_if_cancelled_base
  218. {
  219. promise_throw_if_cancelled_base(bool throw_if_cancelled = true) : throw_if_cancelled_(throw_if_cancelled) {}
  220. // This await transformation determines whether cancellation is propagated as
  221. // an exception.
  222. auto await_transform(this_coro::throw_if_cancelled_0_t)
  223. noexcept
  224. {
  225. return throw_if_cancelled_0_awaitable_{throw_if_cancelled_};
  226. }
  227. // This await transformation sets whether cancellation is propagated as an
  228. // exception.
  229. auto await_transform(this_coro::throw_if_cancelled_1_t throw_if_cancelled)
  230. noexcept
  231. {
  232. return throw_if_cancelled_1_awaitable_{this, throw_if_cancelled.value};
  233. }
  234. bool throw_if_cancelled() const {return throw_if_cancelled_;}
  235. protected:
  236. bool throw_if_cancelled_{true};
  237. struct throw_if_cancelled_0_awaitable_
  238. {
  239. bool value_;
  240. bool await_ready() const noexcept
  241. {
  242. return true;
  243. }
  244. void await_suspend(std::coroutine_handle<void>) noexcept
  245. {
  246. }
  247. auto await_resume()
  248. {
  249. return value_;
  250. }
  251. };
  252. struct throw_if_cancelled_1_awaitable_
  253. {
  254. promise_throw_if_cancelled_base* this_;
  255. bool value_;
  256. bool await_ready() const noexcept
  257. {
  258. return true;
  259. }
  260. void await_suspend(std::coroutine_handle<void>) noexcept
  261. {
  262. }
  263. auto await_resume()
  264. {
  265. this_->throw_if_cancelled_ = value_;
  266. }
  267. };
  268. };
  269. struct promise_memory_resource_base
  270. {
  271. #if !defined(BOOST_COBALT_NO_PMR)
  272. using allocator_type = pmr::polymorphic_allocator<void>;
  273. allocator_type get_allocator() const {return allocator_type{resource};}
  274. template<typename ... Args>
  275. static void * operator new(const std::size_t size, Args & ... args)
  276. {
  277. auto res = detail::get_memory_resource_from_args(args...);
  278. const auto p = res->allocate(size + sizeof(pmr::memory_resource *), alignof(pmr::memory_resource *));
  279. auto pp = static_cast<pmr::memory_resource**>(p);
  280. *pp = res;
  281. return pp + 1;
  282. }
  283. static void operator delete(void * raw, const std::size_t size) noexcept
  284. {
  285. const auto p = static_cast<pmr::memory_resource**>(raw) - 1;
  286. pmr::memory_resource * res = *p;
  287. res->deallocate(p, size + sizeof(pmr::memory_resource *), alignof(pmr::memory_resource *));
  288. }
  289. promise_memory_resource_base(pmr::memory_resource * resource = this_thread::get_default_resource()) : resource(resource) {}
  290. private:
  291. pmr::memory_resource * resource = this_thread::get_default_resource();
  292. #endif
  293. };
  294. /// Allocate the memory and put the allocator behind the cobalt memory
  295. template<typename AllocatorType>
  296. void *allocate_coroutine(const std::size_t size, AllocatorType alloc_)
  297. {
  298. using alloc_type = typename std::allocator_traits<AllocatorType>::template rebind_alloc<unsigned char>;
  299. alloc_type alloc{alloc_};
  300. const std::size_t align_needed = size % alignof(alloc_type);
  301. const std::size_t align_offset = align_needed != 0 ? alignof(alloc_type) - align_needed : 0ull;
  302. const std::size_t alloc_size = size + sizeof(alloc_type) + align_offset;
  303. const auto raw = std::allocator_traits<alloc_type>::allocate(alloc, alloc_size);
  304. new(raw + size + align_offset) alloc_type(std::move(alloc));
  305. return raw;
  306. }
  307. /// Deallocate the memory and destroy the allocator in the cobalt memory.
  308. template<typename AllocatorType>
  309. void deallocate_coroutine(void *raw_, const std::size_t size)
  310. {
  311. using alloc_type = typename std::allocator_traits<AllocatorType>::template rebind_alloc<unsigned char>;
  312. const auto raw = static_cast<unsigned char *>(raw_);
  313. const std::size_t align_needed = size % alignof(alloc_type);
  314. const std::size_t align_offset = align_needed != 0 ? alignof(alloc_type) - align_needed : 0ull;
  315. const std::size_t alloc_size = size + sizeof(alloc_type) + align_offset;
  316. auto alloc_p = reinterpret_cast<alloc_type *>(raw + size + align_offset);
  317. auto alloc = std::move(*alloc_p);
  318. alloc_p->~alloc_type();
  319. using size_type = typename std::allocator_traits<alloc_type>::size_type;
  320. std::allocator_traits<alloc_type>::deallocate(alloc, raw, static_cast<size_type>(alloc_size));
  321. }
  322. template<typename Promise>
  323. struct enable_await_allocator
  324. {
  325. auto await_transform(this_coro::allocator_t)
  326. {
  327. return allocator_awaitable_{static_cast<Promise*>(this)->get_allocator()};
  328. }
  329. private:
  330. struct allocator_awaitable_
  331. {
  332. using allocator_type = typename Promise::allocator_type;
  333. allocator_type alloc;
  334. constexpr static bool await_ready() { return true; }
  335. bool await_suspend( std::coroutine_handle<void> ) { return false; }
  336. allocator_type await_resume()
  337. {
  338. return alloc;
  339. }
  340. };
  341. };
  342. template<typename Promise>
  343. struct enable_await_executor
  344. {
  345. auto await_transform(this_coro::executor_t)
  346. {
  347. return executor_awaitable_{static_cast<Promise*>(this)->get_executor()};
  348. }
  349. private:
  350. struct executor_awaitable_
  351. {
  352. using executor_type = typename Promise::executor_type;
  353. executor_type exec;
  354. constexpr static bool await_ready() { return true; }
  355. bool await_suspend( std::coroutine_handle<void> ) { return false; }
  356. executor_type await_resume()
  357. {
  358. return exec;
  359. }
  360. };
  361. };
  362. }
  363. #endif //BOOST_COBALT_THIS_CORO_HPP