op.hpp 6.6 KB

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