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