• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // detail/impl/strand_executor_service.hpp
3 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 //
5 // Copyright (c) 2003-2021 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 
11 #ifndef BOOST_ASIO_DETAIL_IMPL_STRAND_EXECUTOR_SERVICE_HPP
12 #define BOOST_ASIO_DETAIL_IMPL_STRAND_EXECUTOR_SERVICE_HPP
13 
14 #if defined(_MSC_VER) && (_MSC_VER >= 1200)
15 # pragma once
16 #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
17 
18 #include <boost/asio/detail/fenced_block.hpp>
19 #include <boost/asio/detail/handler_invoke_helpers.hpp>
20 #include <boost/asio/detail/recycling_allocator.hpp>
21 #include <boost/asio/executor_work_guard.hpp>
22 #include <boost/asio/defer.hpp>
23 #include <boost/asio/dispatch.hpp>
24 #include <boost/asio/post.hpp>
25 
26 #include <boost/asio/detail/push_options.hpp>
27 
28 namespace boost {
29 namespace asio {
30 namespace detail {
31 
32 template <typename F, typename Allocator>
33 class strand_executor_service::allocator_binder
34 {
35 public:
36   typedef Allocator allocator_type;
37 
allocator_binder(BOOST_ASIO_MOVE_ARG (F)f,const Allocator & a)38   allocator_binder(BOOST_ASIO_MOVE_ARG(F) f, const Allocator& a)
39     : f_(BOOST_ASIO_MOVE_CAST(F)(f)),
40       allocator_(a)
41   {
42   }
43 
allocator_binder(const allocator_binder & other)44   allocator_binder(const allocator_binder& other)
45     : f_(other.f_),
46       allocator_(other.allocator_)
47   {
48   }
49 
50 #if defined(BOOST_ASIO_HAS_MOVE)
allocator_binder(allocator_binder && other)51   allocator_binder(allocator_binder&& other)
52     : f_(BOOST_ASIO_MOVE_CAST(F)(other.f_)),
53       allocator_(BOOST_ASIO_MOVE_CAST(allocator_type)(other.allocator_))
54   {
55   }
56 #endif // defined(BOOST_ASIO_HAS_MOVE)
57 
get_allocator() const58   allocator_type get_allocator() const BOOST_ASIO_NOEXCEPT
59   {
60     return allocator_;
61   }
62 
operator ()()63   void operator()()
64   {
65     f_();
66   }
67 
68 private:
69   F f_;
70   allocator_type allocator_;
71 };
72 
73 template <typename Executor>
74 class strand_executor_service::invoker<Executor,
75     typename enable_if<
76       execution::is_executor<Executor>::value
77     >::type>
78 {
79 public:
invoker(const implementation_type & impl,Executor & ex)80   invoker(const implementation_type& impl, Executor& ex)
81     : impl_(impl),
82       executor_(boost::asio::prefer(ex, execution::outstanding_work.tracked))
83   {
84   }
85 
invoker(const invoker & other)86   invoker(const invoker& other)
87     : impl_(other.impl_),
88       executor_(other.executor_)
89   {
90   }
91 
92 #if defined(BOOST_ASIO_HAS_MOVE)
invoker(invoker && other)93   invoker(invoker&& other)
94     : impl_(BOOST_ASIO_MOVE_CAST(implementation_type)(other.impl_)),
95       executor_(BOOST_ASIO_MOVE_CAST(executor_type)(other.executor_))
96   {
97   }
98 #endif // defined(BOOST_ASIO_HAS_MOVE)
99 
100   struct on_invoker_exit
101   {
102     invoker* this_;
103 
~on_invoker_exitboost::asio::detail::strand_executor_service::invoker::on_invoker_exit104     ~on_invoker_exit()
105     {
106       if (push_waiting_to_ready(this_->impl_))
107       {
108         recycling_allocator<void> allocator;
109         execution::execute(
110             boost::asio::prefer(
111               boost::asio::require(this_->executor_,
112                 execution::blocking.never),
113             execution::allocator(allocator)),
114             BOOST_ASIO_MOVE_CAST(invoker)(*this_));
115       }
116     }
117   };
118 
operator ()()119   void operator()()
120   {
121     // Ensure the next handler, if any, is scheduled on block exit.
122     on_invoker_exit on_exit = { this };
123     (void)on_exit;
124 
125     run_ready_handlers(impl_);
126   }
127 
128 private:
129   typedef typename decay<
130       typename prefer_result<
131         Executor,
132         execution::outstanding_work_t::tracked_t
133       >::type
134     >::type executor_type;
135 
136   implementation_type impl_;
137   executor_type executor_;
138 };
139 
140 #if !defined(BOOST_ASIO_NO_TS_EXECUTORS)
141 
142 template <typename Executor>
143 class strand_executor_service::invoker<Executor,
144     typename enable_if<
145       !execution::is_executor<Executor>::value
146     >::type>
147 {
148 public:
invoker(const implementation_type & impl,Executor & ex)149   invoker(const implementation_type& impl, Executor& ex)
150     : impl_(impl),
151       work_(ex)
152   {
153   }
154 
invoker(const invoker & other)155   invoker(const invoker& other)
156     : impl_(other.impl_),
157       work_(other.work_)
158   {
159   }
160 
161 #if defined(BOOST_ASIO_HAS_MOVE)
invoker(invoker && other)162   invoker(invoker&& other)
163     : impl_(BOOST_ASIO_MOVE_CAST(implementation_type)(other.impl_)),
164       work_(BOOST_ASIO_MOVE_CAST(executor_work_guard<Executor>)(other.work_))
165   {
166   }
167 #endif // defined(BOOST_ASIO_HAS_MOVE)
168 
169   struct on_invoker_exit
170   {
171     invoker* this_;
172 
~on_invoker_exitboost::asio::detail::strand_executor_service::invoker::on_invoker_exit173     ~on_invoker_exit()
174     {
175       if (push_waiting_to_ready(this_->impl_))
176       {
177         Executor ex(this_->work_.get_executor());
178         recycling_allocator<void> allocator;
179         ex.post(BOOST_ASIO_MOVE_CAST(invoker)(*this_), allocator);
180       }
181     }
182   };
183 
operator ()()184   void operator()()
185   {
186     // Ensure the next handler, if any, is scheduled on block exit.
187     on_invoker_exit on_exit = { this };
188     (void)on_exit;
189 
190     run_ready_handlers(impl_);
191   }
192 
193 private:
194   implementation_type impl_;
195   executor_work_guard<Executor> work_;
196 };
197 
198 #endif // !defined(BOOST_ASIO_NO_TS_EXECUTORS)
199 
200 template <typename Executor, typename Function>
execute(const implementation_type & impl,Executor & ex,BOOST_ASIO_MOVE_ARG (Function)function,typename enable_if<can_query<Executor,execution::allocator_t<void>>::value>::type *)201 inline void strand_executor_service::execute(const implementation_type& impl,
202     Executor& ex, BOOST_ASIO_MOVE_ARG(Function) function,
203     typename enable_if<
204       can_query<Executor, execution::allocator_t<void> >::value
205     >::type*)
206 {
207   return strand_executor_service::do_execute(impl, ex,
208       BOOST_ASIO_MOVE_CAST(Function)(function),
209       boost::asio::query(ex, execution::allocator));
210 }
211 
212 template <typename Executor, typename Function>
execute(const implementation_type & impl,Executor & ex,BOOST_ASIO_MOVE_ARG (Function)function,typename enable_if<!can_query<Executor,execution::allocator_t<void>>::value>::type *)213 inline void strand_executor_service::execute(const implementation_type& impl,
214     Executor& ex, BOOST_ASIO_MOVE_ARG(Function) function,
215     typename enable_if<
216       !can_query<Executor, execution::allocator_t<void> >::value
217     >::type*)
218 {
219   return strand_executor_service::do_execute(impl, ex,
220       BOOST_ASIO_MOVE_CAST(Function)(function),
221       std::allocator<void>());
222 }
223 
224 template <typename Executor, typename Function, typename Allocator>
do_execute(const implementation_type & impl,Executor & ex,BOOST_ASIO_MOVE_ARG (Function)function,const Allocator & a)225 void strand_executor_service::do_execute(const implementation_type& impl,
226     Executor& ex, BOOST_ASIO_MOVE_ARG(Function) function, const Allocator& a)
227 {
228   typedef typename decay<Function>::type function_type;
229 
230   // If the executor is not never-blocking, and we are already in the strand,
231   // then the function can run immediately.
232   if (boost::asio::query(ex, execution::blocking) != execution::blocking.never
233       && running_in_this_thread(impl))
234   {
235     // Make a local, non-const copy of the function.
236     function_type tmp(BOOST_ASIO_MOVE_CAST(Function)(function));
237 
238     fenced_block b(fenced_block::full);
239     boost_asio_handler_invoke_helpers::invoke(tmp, tmp);
240     return;
241   }
242 
243   // Allocate and construct an operation to wrap the function.
244   typedef executor_op<function_type, Allocator> op;
245   typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 };
246   p.p = new (p.v) op(BOOST_ASIO_MOVE_CAST(Function)(function), a);
247 
248   BOOST_ASIO_HANDLER_CREATION((impl->service_->context(), *p.p,
249         "strand_executor", impl.get(), 0, "execute"));
250 
251   // Add the function to the strand and schedule the strand if required.
252   bool first = enqueue(impl, p.p);
253   p.v = p.p = 0;
254   if (first)
255   {
256     execution::execute(ex, invoker<Executor>(impl, ex));
257   }
258 }
259 
260 template <typename Executor, typename Function, typename Allocator>
dispatch(const implementation_type & impl,Executor & ex,BOOST_ASIO_MOVE_ARG (Function)function,const Allocator & a)261 void strand_executor_service::dispatch(const implementation_type& impl,
262     Executor& ex, BOOST_ASIO_MOVE_ARG(Function) function, const Allocator& a)
263 {
264   typedef typename decay<Function>::type function_type;
265 
266   // If we are already in the strand then the function can run immediately.
267   if (running_in_this_thread(impl))
268   {
269     // Make a local, non-const copy of the function.
270     function_type tmp(BOOST_ASIO_MOVE_CAST(Function)(function));
271 
272     fenced_block b(fenced_block::full);
273     boost_asio_handler_invoke_helpers::invoke(tmp, tmp);
274     return;
275   }
276 
277   // Allocate and construct an operation to wrap the function.
278   typedef executor_op<function_type, Allocator> op;
279   typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 };
280   p.p = new (p.v) op(BOOST_ASIO_MOVE_CAST(Function)(function), a);
281 
282   BOOST_ASIO_HANDLER_CREATION((impl->service_->context(), *p.p,
283         "strand_executor", impl.get(), 0, "dispatch"));
284 
285   // Add the function to the strand and schedule the strand if required.
286   bool first = enqueue(impl, p.p);
287   p.v = p.p = 0;
288   if (first)
289   {
290     boost::asio::dispatch(ex,
291         allocator_binder<invoker<Executor>, Allocator>(
292           invoker<Executor>(impl, ex), a));
293   }
294 }
295 
296 // Request invocation of the given function and return immediately.
297 template <typename Executor, typename Function, typename Allocator>
post(const implementation_type & impl,Executor & ex,BOOST_ASIO_MOVE_ARG (Function)function,const Allocator & a)298 void strand_executor_service::post(const implementation_type& impl,
299     Executor& ex, BOOST_ASIO_MOVE_ARG(Function) function, const Allocator& a)
300 {
301   typedef typename decay<Function>::type function_type;
302 
303   // Allocate and construct an operation to wrap the function.
304   typedef executor_op<function_type, Allocator> op;
305   typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 };
306   p.p = new (p.v) op(BOOST_ASIO_MOVE_CAST(Function)(function), a);
307 
308   BOOST_ASIO_HANDLER_CREATION((impl->service_->context(), *p.p,
309         "strand_executor", impl.get(), 0, "post"));
310 
311   // Add the function to the strand and schedule the strand if required.
312   bool first = enqueue(impl, p.p);
313   p.v = p.p = 0;
314   if (first)
315   {
316     boost::asio::post(ex,
317         allocator_binder<invoker<Executor>, Allocator>(
318           invoker<Executor>(impl, ex), a));
319   }
320 }
321 
322 // Request invocation of the given function and return immediately.
323 template <typename Executor, typename Function, typename Allocator>
defer(const implementation_type & impl,Executor & ex,BOOST_ASIO_MOVE_ARG (Function)function,const Allocator & a)324 void strand_executor_service::defer(const implementation_type& impl,
325     Executor& ex, BOOST_ASIO_MOVE_ARG(Function) function, const Allocator& a)
326 {
327   typedef typename decay<Function>::type function_type;
328 
329   // Allocate and construct an operation to wrap the function.
330   typedef executor_op<function_type, Allocator> op;
331   typename op::ptr p = { detail::addressof(a), op::ptr::allocate(a), 0 };
332   p.p = new (p.v) op(BOOST_ASIO_MOVE_CAST(Function)(function), a);
333 
334   BOOST_ASIO_HANDLER_CREATION((impl->service_->context(), *p.p,
335         "strand_executor", impl.get(), 0, "defer"));
336 
337   // Add the function to the strand and schedule the strand if required.
338   bool first = enqueue(impl, p.p);
339   p.v = p.p = 0;
340   if (first)
341   {
342     boost::asio::defer(ex,
343         allocator_binder<invoker<Executor>, Allocator>(
344           invoker<Executor>(impl, ex), a));
345   }
346 }
347 
348 } // namespace detail
349 } // namespace asio
350 } // namespace boost
351 
352 #include <boost/asio/detail/pop_options.hpp>
353 
354 #endif // BOOST_ASIO_DETAIL_IMPL_STRAND_EXECUTOR_SERVICE_HPP
355