handler.hpp 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  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_HANDLER_HPP
  6. #define BOOST_COBALT_HANDLER_HPP
  7. #include <boost/cobalt/this_coro.hpp>
  8. #include <boost/cobalt/unique_handle.hpp>
  9. #include <boost/cobalt/detail/util.hpp>
  10. #include <boost/cobalt/detail/sbo_resource.hpp>
  11. #include <boost/asio/bind_allocator.hpp>
  12. #include <boost/asio/post.hpp>
  13. #include <boost/system/result.hpp>
  14. #include <memory>
  15. #include <optional>
  16. namespace boost::cobalt
  17. {
  18. namespace detail
  19. {
  20. enum class completed_immediately_t
  21. {
  22. no, maybe, yes, initiating
  23. };
  24. struct completion_handler_noop_executor
  25. {
  26. executor exec;
  27. completed_immediately_t * completed_immediately = nullptr;
  28. template<typename Fn>
  29. void execute(Fn && fn) const
  30. {
  31. // only allow it when we're still initializing
  32. if (completed_immediately &&
  33. ((*completed_immediately == completed_immediately_t::initiating)
  34. || (*completed_immediately == completed_immediately_t::maybe)))
  35. {
  36. // only use this indicator if the fn will actually call our completion-handler
  37. // otherwise this was a single op in a composed operation
  38. *completed_immediately = completed_immediately_t::maybe;
  39. fn();
  40. // yes means completion_handler::operator() was called, so we're good.
  41. if (*completed_immediately != completed_immediately_t::yes)
  42. *completed_immediately = completed_immediately_t::initiating;
  43. }
  44. else
  45. {
  46. asio::post(exec, std::forward<Fn>(fn));
  47. }
  48. }
  49. friend bool operator==(const completion_handler_noop_executor&, const completion_handler_noop_executor&) noexcept
  50. {
  51. return true;
  52. }
  53. friend bool operator!=(const completion_handler_noop_executor&, const completion_handler_noop_executor&) noexcept
  54. {
  55. return false;
  56. }
  57. completion_handler_noop_executor(const completion_handler_noop_executor & rhs) noexcept = default;
  58. completion_handler_noop_executor(cobalt::executor inner, completed_immediately_t * completed_immediately)
  59. : exec(std::move(inner)), completed_immediately(completed_immediately)
  60. {
  61. }
  62. };
  63. struct completion_handler_base
  64. {
  65. using cancellation_slot_type = asio::cancellation_slot;
  66. cancellation_slot_type cancellation_slot ;
  67. cancellation_slot_type get_cancellation_slot() const noexcept
  68. {
  69. return cancellation_slot ;
  70. }
  71. using executor_type = executor;
  72. const executor_type & executor_ ;
  73. const executor_type & get_executor() const noexcept
  74. {
  75. return executor_ ;
  76. }
  77. #if !defined(BOOST_COBALT_NO_PMR)
  78. using allocator_type = pmr::polymorphic_allocator<void>;
  79. pmr::polymorphic_allocator<void> allocator ;
  80. allocator_type get_allocator() const noexcept
  81. {
  82. return allocator ;
  83. }
  84. #else
  85. using allocator_type = detail::sbo_allocator<void>;
  86. detail::sbo_allocator<void> allocator ;
  87. allocator_type get_allocator() const noexcept
  88. {
  89. return allocator ;
  90. }
  91. #endif
  92. using immediate_executor_type = completion_handler_noop_executor;
  93. completed_immediately_t * completed_immediately = nullptr;
  94. immediate_executor_type get_immediate_executor() const noexcept
  95. {
  96. return {get_executor(), completed_immediately};
  97. }
  98. template<typename Promise>
  99. requires (requires (Promise p) {{p.get_executor()} -> std::same_as<const executor&>;})
  100. completion_handler_base(std::coroutine_handle<Promise> h,
  101. completed_immediately_t * completed_immediately = nullptr)
  102. : cancellation_slot(asio::get_associated_cancellation_slot(h.promise())),
  103. executor_(h.promise().get_executor()),
  104. #if !defined(BOOST_COBALT_NO_PMR)
  105. allocator(asio::get_associated_allocator(h.promise(), this_thread::get_allocator())),
  106. #else
  107. allocator(detail::get_null_sbo_resource()),
  108. #endif
  109. completed_immediately(completed_immediately)
  110. {
  111. }
  112. #if !defined(BOOST_COBALT_NO_PMR)
  113. template<typename Promise>
  114. requires (requires (Promise p) {{p.get_executor()} -> std::same_as<const executor&>;})
  115. completion_handler_base(std::coroutine_handle<Promise> h,
  116. pmr::memory_resource * resource,
  117. completed_immediately_t * completed_immediately = nullptr)
  118. : cancellation_slot(asio::get_associated_cancellation_slot(h.promise())),
  119. executor_(h.promise().get_executor()),
  120. allocator(resource),
  121. completed_immediately(completed_immediately)
  122. {
  123. }
  124. #else
  125. template<typename Promise>
  126. requires (requires (Promise p) {{p.get_executor()} -> std::same_as<const executor&>;})
  127. completion_handler_base(std::coroutine_handle<Promise> h,
  128. detail::sbo_resource * resource,
  129. completed_immediately_t * completed_immediately = nullptr)
  130. : cancellation_slot(asio::get_associated_cancellation_slot(h.promise())),
  131. executor_(h.promise().get_executor()),
  132. allocator(resource),
  133. completed_immediately(completed_immediately)
  134. {
  135. }
  136. #endif
  137. };
  138. template<typename Handler>
  139. void assign_cancellation(std::coroutine_handle<void>, Handler &&) {}
  140. template<typename Promise, typename Handler>
  141. void assign_cancellation(std::coroutine_handle<Promise> h, Handler && func)
  142. {
  143. if constexpr (requires {h.promise().get_cancellation_slot();})
  144. if (h.promise().get_cancellation_slot().is_connected())
  145. h.promise().get_cancellation_slot().assign(std::forward<Handler>(func));
  146. }
  147. template<typename Promise>
  148. const executor &
  149. get_executor(std::coroutine_handle<Promise> h)
  150. {
  151. if constexpr (requires {h.promise().get_executor();})
  152. {
  153. static_assert(std::same_as<decltype(h.promise().get_executor()),
  154. const executor &>,
  155. "for performance reasons, the get_executor function on a promise must return a const reference");
  156. return h.promise().get_executor();
  157. }
  158. else
  159. return this_thread::get_executor();
  160. }
  161. inline const executor &
  162. get_executor(std::coroutine_handle<>)
  163. {
  164. return this_thread::get_executor();
  165. }
  166. }
  167. template<typename ... Args>
  168. struct handler
  169. {
  170. void operator()(Args ... args)
  171. {
  172. result.emplace(static_cast<Args>(args)...);
  173. }
  174. handler(std::optional<std::tuple<Args...>> &result) : result(result) {}
  175. private:
  176. std::optional<std::tuple<Args...>> &result;
  177. };
  178. template<typename ... Args>
  179. handler(std::optional<std::tuple<Args...>> &result) -> handler<Args...>;
  180. template<typename ... Args>
  181. struct completion_handler : detail::completion_handler_base
  182. {
  183. completion_handler(completion_handler && ) = default;
  184. template<typename Promise>
  185. completion_handler(std::coroutine_handle<Promise> h,
  186. std::optional<std::tuple<Args...>> &result,
  187. detail::completed_immediately_t * completed_immediately = nullptr
  188. #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
  189. , const boost::source_location & loc = BOOST_CURRENT_LOCATION
  190. #endif
  191. ) : completion_handler_base(h, completed_immediately),
  192. self(h.address()), result(result)
  193. #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
  194. , loc_(loc)
  195. #endif
  196. {
  197. }
  198. #if !defined(BOOST_COBALT_NO_PMR)
  199. template<typename Promise>
  200. completion_handler(std::coroutine_handle<Promise> h,
  201. std::optional<std::tuple<Args...>> &result,
  202. pmr::memory_resource * resource,
  203. detail::completed_immediately_t * completed_immediately = nullptr
  204. #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
  205. , const boost::source_location & loc = BOOST_CURRENT_LOCATION
  206. #endif
  207. ) : completion_handler_base(h, resource, completed_immediately),
  208. self(h.address()), result(result)
  209. #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
  210. , loc_(loc)
  211. #endif
  212. {
  213. }
  214. #else
  215. template<typename Promise>
  216. completion_handler(std::coroutine_handle<Promise> h,
  217. std::optional<std::tuple<Args...>> &result,
  218. detail::sbo_resource * resource,
  219. detail::completed_immediately_t * completed_immediately = nullptr)
  220. : completion_handler_base(h, resource, completed_immediately),
  221. self(h.address()), result(result)
  222. {
  223. }
  224. #endif
  225. void operator()(Args ... args)
  226. {
  227. #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
  228. BOOST_ASIO_HANDLER_LOCATION((loc_.file_name(), loc_.line(), loc_.function_name()));
  229. #endif
  230. result.emplace(std::move(args)...);
  231. BOOST_ASSERT(this->self != nullptr);
  232. auto p = this->self.release();
  233. if (completed_immediately != nullptr
  234. && *completed_immediately == detail::completed_immediately_t::maybe)
  235. {
  236. *completed_immediately = detail::completed_immediately_t::yes;
  237. return;
  238. }
  239. std::move(p)();
  240. }
  241. using result_type = std::optional<std::tuple<Args...>>;
  242. ~completion_handler()
  243. {
  244. if (self && completed_immediately
  245. && *completed_immediately == detail::completed_immediately_t::initiating
  246. && std::uncaught_exceptions() > 0)
  247. self.release();
  248. }
  249. private:
  250. unique_handle<void> self;
  251. std::optional<std::tuple<Args...>> &result;
  252. #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
  253. boost::source_location loc_;
  254. #endif
  255. };
  256. };
  257. #endif //BOOST_COBALT_HANDLER_HPP