this_coro.hpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534
  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. #if defined(__cpp_sized_deallocation)
  275. template<typename ... Args>
  276. static void * operator new(const std::size_t size, Args & ... args)
  277. {
  278. auto res = detail::get_memory_resource_from_args(args...);
  279. const auto p = res->allocate(size + sizeof(std::max_align_t));
  280. auto pp = static_cast<pmr::memory_resource**>(p);
  281. *pp = res;
  282. return static_cast<std::max_align_t *>(p) + 1;
  283. }
  284. static void operator delete(void * raw, const std::size_t size) noexcept
  285. {
  286. const auto p = static_cast<std::max_align_t *>(raw) - 1;
  287. pmr::memory_resource * res = *reinterpret_cast<pmr::memory_resource**>(p);
  288. res->deallocate(p, size + sizeof(std::max_align_t));
  289. }
  290. #else
  291. template<typename ... Args>
  292. static void * operator new(const std::size_t size, Args & ... args)
  293. {
  294. using tt = std::pair<pmr::memory_resource *, std::size_t>;
  295. // | memory_resource | size_t | <padding> | coroutine.
  296. constexpr auto block_size = sizeof(tt) / sizeof(std::max_align_t)
  297. + (sizeof(tt) % sizeof(std::max_align_t) ? 1 : 0);
  298. auto res = detail::get_memory_resource_from_args(args...);
  299. const auto p = res->allocate(size + (block_size * sizeof(std::max_align_t)));
  300. new (p) tt(res, size);
  301. return static_cast<std::max_align_t*>(p) + block_size;
  302. }
  303. static void operator delete(void * raw) noexcept
  304. {
  305. using tt = std::pair<pmr::memory_resource *, std::size_t>;
  306. // | memory_resource | size_t | <padding> | coroutine.
  307. constexpr auto block_size = sizeof(tt) / sizeof(std::max_align_t)
  308. + (sizeof(tt) % sizeof(std::max_align_t) ? 1 : 0);
  309. const auto p = static_cast<std::max_align_t*>(raw) - block_size;
  310. const auto tp = *reinterpret_cast<tt*>(p);
  311. const auto res = tp.first;
  312. const auto size = tp.second;
  313. res->deallocate(p, size + (block_size * sizeof(std::max_align_t)));
  314. }
  315. #endif
  316. promise_memory_resource_base(pmr::memory_resource * resource = this_thread::get_default_resource()) : resource(resource) {}
  317. private:
  318. pmr::memory_resource * resource = this_thread::get_default_resource();
  319. #endif
  320. };
  321. #if defined(__cpp_sized_deallocation)
  322. /// Allocate the memory and put the allocator behind the cobalt memory
  323. template<typename AllocatorType>
  324. void *allocate_coroutine(const std::size_t size, AllocatorType alloc_)
  325. {
  326. using alloc_type = typename std::allocator_traits<AllocatorType>::template rebind_alloc<std::max_align_t>;
  327. alloc_type alloc{alloc_};
  328. const std::size_t aligned_size = size / sizeof(std::max_align_t)
  329. + (size % sizeof(std::max_align_t) > 0 ? 1 : 0);
  330. const std::size_t alloc_size = sizeof(AllocatorType) / sizeof(std::max_align_t)
  331. + (sizeof(AllocatorType) % sizeof(std::max_align_t) > 0 ? 1 : 0);
  332. const auto raw = std::allocator_traits<alloc_type>::allocate(alloc, alloc_size + aligned_size);
  333. new(raw + aligned_size) alloc_type(std::move(alloc));
  334. return raw;
  335. }
  336. /// Deallocate the memory and destroy the allocator in the cobalt memory.
  337. template<typename AllocatorType>
  338. void deallocate_coroutine(void *raw_, const std::size_t size)
  339. {
  340. using alloc_type = typename std::allocator_traits<AllocatorType>::template rebind_alloc<std::max_align_t>;
  341. const auto raw = static_cast<std::max_align_t*>(raw_);
  342. const std::size_t aligned_size = size / sizeof(std::max_align_t)
  343. + (size % sizeof(std::max_align_t) > 0 ? 1 : 0);
  344. const std::size_t alloc_size = sizeof(AllocatorType) / sizeof(std::max_align_t)
  345. + (sizeof(AllocatorType) % sizeof(std::max_align_t) > 0 ? 1 : 0);
  346. auto alloc_p = reinterpret_cast<alloc_type *>(raw + aligned_size);
  347. auto alloc = std::move(*alloc_p);
  348. alloc_p->~alloc_type();
  349. std::allocator_traits<alloc_type>::deallocate(alloc, raw, aligned_size + alloc_size);
  350. }
  351. #else
  352. /// Allocate the memory and put the allocator behind the cobalt memory
  353. template<typename AllocatorType>
  354. void *allocate_coroutine(const std::size_t size, AllocatorType alloc_)
  355. {
  356. using alloc_type = typename std::allocator_traits<AllocatorType>::template rebind_alloc<std::max_align_t>;
  357. alloc_type alloc{alloc_};
  358. const std::size_t aligned_size = size / sizeof(std::max_align_t)
  359. + (size % sizeof(std::max_align_t) > 0 ? 1 : 0);
  360. const std::size_t alloc_size = sizeof(AllocatorType) / sizeof(std::max_align_t)
  361. + (sizeof(AllocatorType) % sizeof(std::max_align_t) > 0 ? 1 : 0);
  362. const std::size_t size_size = sizeof(std::size_t) / sizeof(std::max_align_t)
  363. + (sizeof(std::size_t) % sizeof(std::max_align_t) > 0 ? 1 : 0);
  364. static_assert(alignof(std::max_align_t) >= sizeof(std::size_t));
  365. const auto raw = std::allocator_traits<alloc_type>::allocate(alloc, alloc_size + aligned_size + size_size);
  366. new(raw) alloc_type(std::move(alloc));
  367. new(raw + alloc_size) std::size_t(aligned_size);
  368. return raw + alloc_size + size_size;
  369. }
  370. /// Deallocate the memory and destroy the allocator in the cobalt memory.
  371. template<typename AllocatorType>
  372. void deallocate_coroutine(void *raw_)
  373. {
  374. using alloc_type = typename std::allocator_traits<AllocatorType>::template rebind_alloc<std::max_align_t>;
  375. const auto raw = static_cast<std::max_align_t*>(raw_);
  376. const std::size_t size_size = sizeof(std::size_t) / sizeof(std::max_align_t)
  377. + (sizeof(std::size_t) % sizeof(std::max_align_t) > 0 ? 1 : 0);
  378. const std::size_t aligned_size = *reinterpret_cast<std::size_t *>(raw - size_size);
  379. const std::size_t alloc_size = sizeof(AllocatorType) / sizeof(std::max_align_t)
  380. + (sizeof(AllocatorType) % sizeof(std::max_align_t) > 0 ? 1 : 0);
  381. auto alloc_p = reinterpret_cast<alloc_type *>(raw - alloc_size - size_size);
  382. auto alloc = std::move(*alloc_p);
  383. alloc_p->~alloc_type();
  384. std::allocator_traits<alloc_type>::deallocate(alloc, raw - alloc_size - size_size, aligned_size + alloc_size + size_size);
  385. }
  386. #endif
  387. template<typename Promise>
  388. struct enable_await_allocator
  389. {
  390. auto await_transform(this_coro::allocator_t)
  391. {
  392. return allocator_awaitable_{static_cast<Promise*>(this)->get_allocator()};
  393. }
  394. private:
  395. struct allocator_awaitable_
  396. {
  397. using allocator_type = typename Promise::allocator_type;
  398. allocator_type alloc;
  399. constexpr static bool await_ready() { return true; }
  400. bool await_suspend( std::coroutine_handle<void> ) { return false; }
  401. allocator_type await_resume()
  402. {
  403. return alloc;
  404. }
  405. };
  406. };
  407. template<typename Promise>
  408. struct enable_await_executor
  409. {
  410. auto await_transform(this_coro::executor_t)
  411. {
  412. return executor_awaitable_{static_cast<Promise*>(this)->get_executor()};
  413. }
  414. private:
  415. struct executor_awaitable_
  416. {
  417. using executor_type = typename Promise::executor_type;
  418. executor_type exec;
  419. constexpr static bool await_ready() { return true; }
  420. bool await_suspend( std::coroutine_handle<void> ) { return false; }
  421. executor_type await_resume()
  422. {
  423. return exec;
  424. }
  425. };
  426. };
  427. }
  428. #endif //BOOST_COBALT_THIS_CORO_HPP