strand_executor_service.hpp 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. //
  2. // detail/impl/strand_executor_service.hpp
  3. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  4. //
  5. // Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com)
  6. //
  7. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  8. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  9. //
  10. #ifndef BOOST_ASIO_DETAIL_IMPL_STRAND_EXECUTOR_SERVICE_HPP
  11. #define BOOST_ASIO_DETAIL_IMPL_STRAND_EXECUTOR_SERVICE_HPP
  12. #if defined(_MSC_VER) && (_MSC_VER >= 1200)
  13. # pragma once
  14. #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
  15. #include <boost/asio/detail/fenced_block.hpp>
  16. #include <boost/asio/detail/recycling_allocator.hpp>
  17. #include <boost/asio/executor_work_guard.hpp>
  18. #include <boost/asio/defer.hpp>
  19. #include <boost/asio/dispatch.hpp>
  20. #include <boost/asio/post.hpp>
  21. #include <boost/asio/detail/push_options.hpp>
  22. namespace boost {
  23. namespace asio {
  24. namespace detail {
  25. template <typename F, typename Allocator>
  26. class strand_executor_service::allocator_binder
  27. {
  28. public:
  29. typedef Allocator allocator_type;
  30. allocator_binder(F&& f, const Allocator& a)
  31. : f_(static_cast<F&&>(f)),
  32. allocator_(a)
  33. {
  34. }
  35. allocator_binder(const allocator_binder& other)
  36. : f_(other.f_),
  37. allocator_(other.allocator_)
  38. {
  39. }
  40. allocator_binder(allocator_binder&& other)
  41. : f_(static_cast<F&&>(other.f_)),
  42. allocator_(static_cast<allocator_type&&>(other.allocator_))
  43. {
  44. }
  45. allocator_type get_allocator() const noexcept
  46. {
  47. return allocator_;
  48. }
  49. void operator()()
  50. {
  51. f_();
  52. }
  53. private:
  54. F f_;
  55. allocator_type allocator_;
  56. };
  57. template <typename Executor>
  58. class strand_executor_service::invoker<Executor,
  59. enable_if_t<
  60. execution::is_executor<Executor>::value
  61. >>
  62. {
  63. public:
  64. invoker(const implementation_type& impl, Executor& ex)
  65. : impl_(impl),
  66. executor_(boost::asio::prefer(ex, execution::outstanding_work.tracked))
  67. {
  68. }
  69. invoker(const invoker& other)
  70. : impl_(other.impl_),
  71. executor_(other.executor_)
  72. {
  73. }
  74. invoker(invoker&& other)
  75. : impl_(static_cast<implementation_type&&>(other.impl_)),
  76. executor_(static_cast<executor_type&&>(other.executor_))
  77. {
  78. }
  79. struct on_invoker_exit
  80. {
  81. invoker* this_;
  82. ~on_invoker_exit()
  83. {
  84. if (push_waiting_to_ready(this_->impl_))
  85. {
  86. recycling_allocator<void> allocator;
  87. executor_type ex = this_->executor_;
  88. boost::asio::prefer(
  89. boost::asio::require(
  90. static_cast<executor_type&&>(ex),
  91. execution::blocking.never),
  92. execution::allocator(allocator)
  93. ).execute(static_cast<invoker&&>(*this_));
  94. }
  95. }
  96. };
  97. void operator()()
  98. {
  99. // Ensure the next handler, if any, is scheduled on block exit.
  100. on_invoker_exit on_exit = { this };
  101. (void)on_exit;
  102. run_ready_handlers(impl_);
  103. }
  104. private:
  105. typedef decay_t<
  106. prefer_result_t<
  107. Executor,
  108. execution::outstanding_work_t::tracked_t
  109. >
  110. > executor_type;
  111. implementation_type impl_;
  112. executor_type executor_;
  113. };
  114. #if !defined(BOOST_ASIO_NO_TS_EXECUTORS)
  115. template <typename Executor>
  116. class strand_executor_service::invoker<Executor,
  117. enable_if_t<
  118. !execution::is_executor<Executor>::value
  119. >>
  120. {
  121. public:
  122. invoker(const implementation_type& impl, Executor& ex)
  123. : impl_(impl),
  124. work_(ex)
  125. {
  126. }
  127. invoker(const invoker& other)
  128. : impl_(other.impl_),
  129. work_(other.work_)
  130. {
  131. }
  132. invoker(invoker&& other)
  133. : impl_(static_cast<implementation_type&&>(other.impl_)),
  134. work_(static_cast<executor_work_guard<Executor>&&>(other.work_))
  135. {
  136. }
  137. struct on_invoker_exit
  138. {
  139. invoker* this_;
  140. ~on_invoker_exit()
  141. {
  142. if (push_waiting_to_ready(this_->impl_))
  143. {
  144. Executor ex(this_->work_.get_executor());
  145. recycling_allocator<void> allocator;
  146. ex.post(static_cast<invoker&&>(*this_), allocator);
  147. }
  148. }
  149. };
  150. void operator()()
  151. {
  152. // Ensure the next handler, if any, is scheduled on block exit.
  153. on_invoker_exit on_exit = { this };
  154. (void)on_exit;
  155. run_ready_handlers(impl_);
  156. }
  157. private:
  158. implementation_type impl_;
  159. executor_work_guard<Executor> work_;
  160. };
  161. #endif // !defined(BOOST_ASIO_NO_TS_EXECUTORS)
  162. template <typename Executor, typename Function>
  163. inline void strand_executor_service::execute(const implementation_type& impl,
  164. Executor& ex, Function&& function,
  165. enable_if_t<
  166. can_query<Executor, execution::allocator_t<void>>::value
  167. >*)
  168. {
  169. return strand_executor_service::do_execute(impl, ex,
  170. static_cast<Function&&>(function),
  171. boost::asio::query(ex, execution::allocator));
  172. }
  173. template <typename Executor, typename Function>
  174. inline void strand_executor_service::execute(const implementation_type& impl,
  175. Executor& ex, Function&& function,
  176. enable_if_t<
  177. !can_query<Executor, execution::allocator_t<void>>::value
  178. >*)
  179. {
  180. return strand_executor_service::do_execute(impl, ex,
  181. static_cast<Function&&>(function),
  182. std::allocator<void>());
  183. }
  184. template <typename Executor, typename Function, typename Allocator>
  185. void strand_executor_service::do_execute(const implementation_type& impl,
  186. Executor& ex, Function&& function, const Allocator& a)
  187. {
  188. typedef decay_t<Function> function_type;
  189. // If the executor is not never-blocking, and we are already in the strand,
  190. // then the function can run immediately.
  191. if (boost::asio::query(ex, execution::blocking) != execution::blocking.never
  192. && running_in_this_thread(impl))
  193. {
  194. // Make a local, non-const copy of the function.
  195. function_type tmp(static_cast<Function&&>(function));
  196. fenced_block b(fenced_block::full);
  197. static_cast<function_type&&>(tmp)();
  198. return;
  199. }
  200. // Allocate and construct an operation to wrap the function.
  201. typedef executor_op<function_type, Allocator> op;
  202. typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 };
  203. p.p = new (p.v) op(static_cast<Function&&>(function), a);
  204. BOOST_ASIO_HANDLER_CREATION((impl->service_->context(), *p.p,
  205. "strand_executor", impl.get(), 0, "execute"));
  206. // Add the function to the strand and schedule the strand if required.
  207. bool first = enqueue(impl, p.p);
  208. p.v = p.p = 0;
  209. if (first)
  210. {
  211. ex.execute(invoker<Executor>(impl, ex));
  212. }
  213. }
  214. template <typename Executor, typename Function, typename Allocator>
  215. void strand_executor_service::dispatch(const implementation_type& impl,
  216. Executor& ex, Function&& function, const Allocator& a)
  217. {
  218. typedef decay_t<Function> function_type;
  219. // If we are already in the strand then the function can run immediately.
  220. if (running_in_this_thread(impl))
  221. {
  222. // Make a local, non-const copy of the function.
  223. function_type tmp(static_cast<Function&&>(function));
  224. fenced_block b(fenced_block::full);
  225. static_cast<function_type&&>(tmp)();
  226. return;
  227. }
  228. // Allocate and construct an operation to wrap the function.
  229. typedef executor_op<function_type, Allocator> op;
  230. typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 };
  231. p.p = new (p.v) op(static_cast<Function&&>(function), a);
  232. BOOST_ASIO_HANDLER_CREATION((impl->service_->context(), *p.p,
  233. "strand_executor", impl.get(), 0, "dispatch"));
  234. // Add the function to the strand and schedule the strand if required.
  235. bool first = enqueue(impl, p.p);
  236. p.v = p.p = 0;
  237. if (first)
  238. {
  239. boost::asio::dispatch(ex,
  240. allocator_binder<invoker<Executor>, Allocator>(
  241. invoker<Executor>(impl, ex), a));
  242. }
  243. }
  244. // Request invocation of the given function and return immediately.
  245. template <typename Executor, typename Function, typename Allocator>
  246. void strand_executor_service::post(const implementation_type& impl,
  247. Executor& ex, Function&& function, const Allocator& a)
  248. {
  249. typedef decay_t<Function> function_type;
  250. // Allocate and construct an operation to wrap the function.
  251. typedef executor_op<function_type, Allocator> op;
  252. typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 };
  253. p.p = new (p.v) op(static_cast<Function&&>(function), a);
  254. BOOST_ASIO_HANDLER_CREATION((impl->service_->context(), *p.p,
  255. "strand_executor", impl.get(), 0, "post"));
  256. // Add the function to the strand and schedule the strand if required.
  257. bool first = enqueue(impl, p.p);
  258. p.v = p.p = 0;
  259. if (first)
  260. {
  261. boost::asio::post(ex,
  262. allocator_binder<invoker<Executor>, Allocator>(
  263. invoker<Executor>(impl, ex), a));
  264. }
  265. }
  266. // Request invocation of the given function and return immediately.
  267. template <typename Executor, typename Function, typename Allocator>
  268. void strand_executor_service::defer(const implementation_type& impl,
  269. Executor& ex, Function&& function, const Allocator& a)
  270. {
  271. typedef decay_t<Function> function_type;
  272. // Allocate and construct an operation to wrap the function.
  273. typedef executor_op<function_type, Allocator> op;
  274. typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 };
  275. p.p = new (p.v) op(static_cast<Function&&>(function), a);
  276. BOOST_ASIO_HANDLER_CREATION((impl->service_->context(), *p.p,
  277. "strand_executor", impl.get(), 0, "defer"));
  278. // Add the function to the strand and schedule the strand if required.
  279. bool first = enqueue(impl, p.p);
  280. p.v = p.p = 0;
  281. if (first)
  282. {
  283. boost::asio::defer(ex,
  284. allocator_binder<invoker<Executor>, Allocator>(
  285. invoker<Executor>(impl, ex), a));
  286. }
  287. }
  288. } // namespace detail
  289. } // namespace asio
  290. } // namespace boost
  291. #include <boost/asio/detail/pop_options.hpp>
  292. #endif // BOOST_ASIO_DETAIL_IMPL_STRAND_EXECUTOR_SERVICE_HPP