op.hpp 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  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_OP_HPP
  8. #define BOOST_COBALT_OP_HPP
  9. #include <boost/cobalt/detail/handler.hpp>
  10. #include <boost/cobalt/detail/sbo_resource.hpp>
  11. #include <boost/cobalt/result.hpp>
  12. #include <boost/core/no_exceptions_support.hpp>
  13. #include <boost/config.hpp>
  14. #include <boost/asio/deferred.hpp>
  15. namespace boost::cobalt
  16. {
  17. template<typename ... Args>
  18. struct BOOST_SYMBOL_VISIBLE op
  19. {
  20. virtual void ready(cobalt::handler<Args...>) {};
  21. virtual void initiate(cobalt::completion_handler<Args...> complete) = 0 ;
  22. virtual ~op() = default;
  23. struct awaitable_base
  24. {
  25. op<Args...> &op_;
  26. std::optional<std::tuple<Args...>> result;
  27. #if !defined(BOOST_COBALT_NO_PMR)
  28. using resource_type = pmr::memory_resource;
  29. #else
  30. using resource_type = detail::sbo_resource;
  31. #endif
  32. awaitable_base(op<Args...> * op_, resource_type *resource) : op_(*op_), resource(resource) {}
  33. awaitable_base(awaitable_base && lhs) noexcept = default;
  34. #if defined(_MSC_VER)
  35. BOOST_NOINLINE ~awaitable_base() {}
  36. #endif
  37. bool await_ready()
  38. {
  39. op_.ready(handler<Args...>(result));
  40. return result.has_value();
  41. }
  42. detail::completed_immediately_t completed_immediately = detail::completed_immediately_t::no;
  43. std::exception_ptr init_ep;
  44. resource_type *resource;
  45. template<typename Promise>
  46. bool await_suspend(std::coroutine_handle<Promise> h
  47. #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
  48. , const boost::source_location & loc = BOOST_CURRENT_LOCATION
  49. #endif
  50. ) noexcept
  51. {
  52. BOOST_TRY
  53. {
  54. completed_immediately = detail::completed_immediately_t::initiating;
  55. #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
  56. op_.initiate(completion_handler<Args...>{h, result, resource, &completed_immediately, loc});
  57. #else
  58. op_.initiate(completion_handler<Args...>{h, result, resource, &completed_immediately});
  59. #endif
  60. if (completed_immediately == detail::completed_immediately_t::initiating)
  61. completed_immediately = detail::completed_immediately_t::no;
  62. return completed_immediately != detail::completed_immediately_t::yes;
  63. }
  64. BOOST_CATCH(...)
  65. {
  66. init_ep = std::current_exception();
  67. return false;
  68. }
  69. BOOST_CATCH_END
  70. }
  71. BOOST_COBALT_MSVC_NOINLINE
  72. auto await_resume(const boost::source_location & loc = BOOST_CURRENT_LOCATION)
  73. {
  74. if (init_ep)
  75. std::rethrow_exception(init_ep);
  76. return await_resume(as_result_tag{}).value(loc);
  77. }
  78. #if defined(_MSC_VER)
  79. BOOST_NOINLINE
  80. #endif
  81. auto await_resume(const struct as_tuple_tag &)
  82. {
  83. if (init_ep)
  84. std::rethrow_exception(init_ep);
  85. return *std::move(result);
  86. }
  87. #if defined(_MSC_VER)
  88. BOOST_NOINLINE
  89. #endif
  90. auto await_resume(const struct as_result_tag &)
  91. {
  92. if (init_ep)
  93. std::rethrow_exception(init_ep);
  94. return interpret_as_result(*std::move(result));
  95. }
  96. };
  97. struct awaitable : awaitable_base
  98. {
  99. char buffer[BOOST_COBALT_SBO_BUFFER_SIZE];
  100. detail::sbo_resource resource{buffer, sizeof(buffer)};
  101. awaitable(op<Args...> * op_) : awaitable_base(op_, &resource) {}
  102. awaitable(awaitable && rhs) : awaitable_base(std::move(rhs))
  103. {
  104. this->awaitable_base::resource = &resource;
  105. }
  106. awaitable_base replace_resource(typename awaitable_base::resource_type * resource) &&
  107. {
  108. awaitable_base nw = std::move(*this);
  109. nw.resource = resource;
  110. return nw;
  111. }
  112. };
  113. awaitable operator co_await()
  114. {
  115. return awaitable{this};
  116. }
  117. };
  118. struct use_op_t
  119. {
  120. /// Default constructor.
  121. constexpr use_op_t()
  122. {
  123. }
  124. /// Adapts an executor to add the @c use_op_t completion token as the
  125. /// default.
  126. template <typename InnerExecutor>
  127. struct executor_with_default : InnerExecutor
  128. {
  129. /// Specify @c use_op_t as the default completion token type.
  130. typedef use_op_t default_completion_token_type;
  131. executor_with_default(const InnerExecutor& ex) noexcept
  132. : InnerExecutor(ex)
  133. {
  134. }
  135. /// Construct the adapted executor from the inner executor type.
  136. template <typename InnerExecutor1>
  137. executor_with_default(const InnerExecutor1& ex,
  138. typename std::enable_if<
  139. std::conditional<
  140. !std::is_same<InnerExecutor1, executor_with_default>::value,
  141. std::is_convertible<InnerExecutor1, InnerExecutor>,
  142. std::false_type
  143. >::type::value>::type = 0) noexcept
  144. : InnerExecutor(ex)
  145. {
  146. }
  147. };
  148. /// Type alias to adapt an I/O object to use @c use_op_t as its
  149. /// default completion token type.
  150. template <typename T>
  151. using as_default_on_t = typename T::template rebind_executor<
  152. executor_with_default<typename T::executor_type> >::other;
  153. /// Function helper to adapt an I/O object to use @c use_op_t as its
  154. /// default completion token type.
  155. template <typename T>
  156. static typename std::decay_t<T>::template rebind_executor<
  157. executor_with_default<typename std::decay_t<T>::executor_type>
  158. >::other
  159. as_default_on(T && object)
  160. {
  161. return typename std::decay_t<T>::template rebind_executor<
  162. executor_with_default<typename std::decay_t<T>::executor_type>
  163. >::other(std::forward<T>(object));
  164. }
  165. };
  166. constexpr use_op_t use_op{};
  167. struct enable_await_deferred
  168. {
  169. template<typename ... Args, typename Initiation, typename ... InitArgs>
  170. auto await_transform(asio::deferred_async_operation<void(Args...), Initiation, InitArgs...> op_)
  171. {
  172. struct deferred_op : op<Args...>
  173. {
  174. asio::deferred_async_operation<void(Args...), Initiation, InitArgs...> op_;
  175. deferred_op(asio::deferred_async_operation<void(Args...), Initiation, InitArgs...> op_)
  176. : op_(std::move(op_)) {}
  177. void initiate(cobalt::completion_handler<Args...> complete) override
  178. {
  179. std::move(op_)(std::move(complete));
  180. }
  181. };
  182. return deferred_op{std::move(op_)};
  183. }
  184. };
  185. }
  186. namespace boost::asio
  187. {
  188. template<typename ... Args>
  189. struct async_result<boost::cobalt::use_op_t, void(Args...)>
  190. {
  191. using return_type = boost::cobalt::op<Args...>;
  192. template <typename Initiation, typename... InitArgs>
  193. struct op_impl final : boost::cobalt::op<Args...>
  194. {
  195. Initiation initiation;
  196. std::tuple<InitArgs...> args;
  197. template<typename Initiation_, typename ...InitArgs_>
  198. op_impl(Initiation_ initiation,
  199. InitArgs_ && ... args)
  200. : initiation(std::forward<Initiation_>(initiation))
  201. , args(std::forward<InitArgs_>(args)...) {}
  202. void initiate(cobalt::completion_handler<Args...> complete) final override
  203. {
  204. std::apply(
  205. [&](InitArgs && ... args)
  206. {
  207. std::move(initiation)(std::move(complete),
  208. std::move(args)...);
  209. }, std::move(args));
  210. }
  211. };
  212. template <typename Initiation, typename... InitArgs>
  213. static auto initiate(Initiation && initiation,
  214. boost::cobalt::use_op_t,
  215. InitArgs &&... args)
  216. -> op_impl<std::decay_t<Initiation>, std::decay_t<InitArgs>...>
  217. {
  218. return op_impl<std::decay_t<Initiation>, std::decay_t<InitArgs>...>(
  219. std::forward<Initiation>(initiation),
  220. std::forward<InitArgs>(args)...);
  221. }
  222. };
  223. }
  224. #endif //BOOST_COBALT_OP_HPP