composition.hpp 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. //
  2. // Copyright (c) 2024 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_EXPERIMENTAL_COMPOSITION_HPP
  8. #define BOOST_COBALT_EXPERIMENTAL_COMPOSITION_HPP
  9. #include <boost/cobalt/op.hpp>
  10. #include <boost/cobalt/detail/sbo_resource.hpp>
  11. namespace boost::cobalt::detail
  12. {
  13. template<typename ... Args>
  14. struct composition_promise
  15. :
  16. promise_cancellation_base<asio::cancellation_slot, asio::enable_total_cancellation>,
  17. enable_await_allocator<composition_promise<Args...>>,
  18. enable_await_executor<composition_promise<Args...>>
  19. {
  20. void get_return_object() {}
  21. using promise_cancellation_base<asio::cancellation_slot, asio::enable_total_cancellation>::await_transform;
  22. using enable_await_allocator<composition_promise<Args...>>::await_transform;
  23. using enable_await_executor<composition_promise<Args...>>::await_transform;
  24. using handler_type = completion_handler<Args...>;
  25. using allocator_type = typename handler_type::allocator_type;
  26. allocator_type get_allocator() const {return handler.get_allocator();}
  27. #if !defined(BOOST_COBALT_NO_PMR)
  28. using resource_type = pmr::memory_resource;
  29. #else
  30. using resource_type = cobalt::detail::sbo_resource;
  31. #endif
  32. template<typename ... Args_, typename Initiation, typename ... InitArgs>
  33. BOOST_COBALT_MSVC_NOINLINE
  34. auto await_transform(asio::deferred_async_operation<void(Args_...), Initiation, InitArgs...> op_)
  35. {
  36. struct deferred_op : op<Args_...>
  37. {
  38. asio::deferred_async_operation<void(Args_...), Initiation, InitArgs...> op_;
  39. deferred_op(asio::deferred_async_operation<void(Args_...), Initiation, InitArgs...> op_,
  40. resource_type * resource)
  41. : op_(std::move(op_)), resource(resource) {}
  42. void initiate(cobalt::completion_handler<Args_...> complete) override
  43. {
  44. std::move(op_)(std::move(complete));
  45. }
  46. resource_type * resource;
  47. typename op<Args_...>::awaitable_base operator co_await()
  48. {
  49. return static_cast<op<Args_...>&&>(*this).operator co_await().replace_resource(this->resource);
  50. }
  51. };
  52. return cobalt::as_tuple(deferred_op{std::move(op_), handler.get_allocator().resource()});
  53. }
  54. template<typename Op>
  55. requires requires (Op && op, resource_type* res)
  56. {
  57. {static_cast<Op>(op).operator co_await().replace_resource(res)} -> awaitable_type<composition_promise>;
  58. }
  59. BOOST_COBALT_MSVC_NOINLINE
  60. auto await_transform(Op && op_)
  61. {
  62. struct replacing_op
  63. {
  64. Op op;
  65. resource_type * resource;
  66. auto operator co_await()
  67. {
  68. return std::forward<Op>(op).operator co_await().replace_resource(this->resource);
  69. }
  70. };
  71. return cobalt::as_tuple(replacing_op{std::forward<Op>(op_), handler.get_allocator().resource()});
  72. }
  73. using executor_type = typename handler_type::executor_type ;
  74. const executor & get_executor() const {return handler.get_executor();}
  75. template<typename ... Ts>
  76. static void * operator new(const std::size_t size, Ts & ... args)
  77. {
  78. using tt = std::pair<resource_type *, std::size_t>;
  79. // | memory_resource | size_t | <padding> | coroutine.
  80. constexpr auto block_size = sizeof(tt) / sizeof(std::max_align_t)
  81. + (sizeof(tt) % sizeof(std::max_align_t) ? 1 : 0);
  82. auto res = std::get<sizeof... (Ts) - 1>(std::tie(args...)).get_allocator().resource();
  83. const auto p = res->allocate(size + (block_size * sizeof(std::max_align_t)));
  84. new (p) tt(res, size);
  85. return static_cast<std::max_align_t*>(p) + block_size;
  86. }
  87. static void operator delete(void * raw) noexcept
  88. {
  89. using tt = std::pair<resource_type *, std::size_t>;
  90. // | memory_resource | size_t | <padding> | coroutine.
  91. constexpr auto block_size = sizeof(tt) / sizeof(std::max_align_t)
  92. + (sizeof(tt) % sizeof(std::max_align_t) ? 1 : 0);
  93. const auto p = static_cast<std::max_align_t*>(raw) - block_size;
  94. const auto tp = *reinterpret_cast<tt*>(p);
  95. const auto res = tp.first;
  96. const auto size = tp.second;
  97. res->deallocate(p, size + (block_size * sizeof(std::max_align_t)));
  98. }
  99. completion_handler<Args...> handler;
  100. template<typename ... Ts>
  101. composition_promise(Ts && ... args) : handler(std::move(std::get<sizeof... (Ts) - 1>(std::tie(args...))))
  102. {
  103. }
  104. void unhandled_exception() { throw ; }
  105. constexpr static std::suspend_never initial_suspend() {return {};}
  106. void return_value(std::tuple<Args ...> args)
  107. {
  108. handler.result.emplace(std::move(args));
  109. }
  110. struct final_awaitable
  111. {
  112. constexpr bool await_ready() noexcept {return false;}
  113. completion_handler<Args...> handler;
  114. BOOST_COBALT_MSVC_NOINLINE
  115. std::coroutine_handle<void> await_suspend(std::coroutine_handle<composition_promise> h) noexcept
  116. {
  117. auto exec = handler.get_executor();
  118. auto ho = handler.self.release();
  119. detail::self_destroy(h, exec);
  120. if (handler.completed_immediately != nullptr
  121. && *handler.completed_immediately == completed_immediately_t::initiating)
  122. //not maybe here, because we don't go through the immediate executor
  123. {
  124. *handler.completed_immediately = completed_immediately_t::yes;
  125. return std::noop_coroutine();
  126. }
  127. return ho;
  128. }
  129. constexpr void await_resume() noexcept {}
  130. };
  131. BOOST_COBALT_MSVC_NOINLINE
  132. auto final_suspend() noexcept
  133. {
  134. return final_awaitable{std::move(handler)};
  135. }
  136. };
  137. }
  138. template<typename ... Args>
  139. struct std::coroutine_traits<void, boost::cobalt::completion_handler<Args...>>
  140. {
  141. using promise_type = ::boost::cobalt::detail::composition_promise<Args...>;
  142. };
  143. template<typename T0, typename ... Args>
  144. struct std::coroutine_traits<void, T0, boost::cobalt::completion_handler<Args...>>
  145. {
  146. using promise_type = ::boost::cobalt::detail::composition_promise<Args...>;
  147. };
  148. template<typename T0, typename T1, typename ... Args>
  149. struct std::coroutine_traits<void, T0, T1, boost::cobalt::completion_handler<Args...>>
  150. {
  151. using promise_type = ::boost::cobalt::detail::composition_promise<Args...>;
  152. };
  153. template<typename T0, typename T1, typename T2, typename ... Args>
  154. struct std::coroutine_traits<void, T0, T1, T2, boost::cobalt::completion_handler<Args...>>
  155. {
  156. using promise_type = ::boost::cobalt::detail::composition_promise<Args...>;
  157. };
  158. template<typename T0, typename T1, typename T2, typename T3, typename ... Args>
  159. struct std::coroutine_traits<void, T0, T1, T2, T3, boost::cobalt::completion_handler<Args...>>
  160. {
  161. using promise_type = ::boost::cobalt::detail::composition_promise<Args...>;
  162. };
  163. template<typename T0, typename T1, typename T2, typename T3, typename T4, typename ... Args>
  164. struct std::coroutine_traits<void, T0, T1, T2, T3, T4, boost::cobalt::completion_handler<Args...>>
  165. {
  166. using promise_type = ::boost::cobalt::detail::composition_promise<Args...>;
  167. };
  168. template<typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename ... Args>
  169. struct std::coroutine_traits<void, T0, T1, T2, T3, T4, T5, boost::cobalt::completion_handler<Args...>>
  170. {
  171. using promise_type = ::boost::cobalt::detail::composition_promise<Args...>;
  172. };
  173. template<typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename ... Args>
  174. struct std::coroutine_traits<void, T0, T1, T2, T3, T4, T5, T6, boost::cobalt::completion_handler<Args...>>
  175. {
  176. using promise_type = ::boost::cobalt::detail::composition_promise<Args...>;
  177. };
  178. template<typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename ... Args>
  179. struct std::coroutine_traits<void, T0, T1, T2, T3, T4, T5, T6, T7, boost::cobalt::completion_handler<Args...>>
  180. {
  181. using promise_type = ::boost::cobalt::detail::composition_promise<Args...>;
  182. };
  183. template<typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename ... Args>
  184. struct std::coroutine_traits<void, T0, T1, T2, T3, T4, T5, T6, T7, T8, boost::cobalt::completion_handler<Args...>>
  185. {
  186. using promise_type = ::boost::cobalt::detail::composition_promise<Args...>;
  187. };
  188. #endif //BOOST_COBALT_EXPERIMENTAL_COMPOSITION_HPP