1 //
2 // detail/handler_work.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_HANDLER_WORK_HPP
12 #define BOOST_ASIO_DETAIL_HANDLER_WORK_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/config.hpp>
19 #include <boost/asio/associated_executor.hpp>
20 #include <boost/asio/detail/handler_invoke_helpers.hpp>
21 #include <boost/asio/detail/type_traits.hpp>
22 #include <boost/asio/execution/allocator.hpp>
23 #include <boost/asio/execution/blocking.hpp>
24 #include <boost/asio/execution/execute.hpp>
25 #include <boost/asio/execution/executor.hpp>
26 #include <boost/asio/execution/outstanding_work.hpp>
27 #include <boost/asio/executor_work_guard.hpp>
28 #include <boost/asio/prefer.hpp>
29 
30 #include <boost/asio/detail/push_options.hpp>
31 
32 namespace boost {
33 namespace asio {
34 
35 class executor;
36 class io_context;
37 
38 #if !defined(BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT)
39 
40 class any_io_executor;
41 
42 #endif // !defined(BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT)
43 
44 namespace execution {
45 
46 #if defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
47 
48 template <typename...> class any_executor;
49 
50 #else // defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
51 
52 template <typename, typename, typename, typename, typename,
53     typename, typename, typename, typename> class any_executor;
54 
55 #endif // defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
56 
57 } // namespace execution
58 namespace detail {
59 
60 template <typename Executor, typename CandidateExecutor = void,
61     typename IoContext = io_context,
62     typename PolymorphicExecutor = executor, typename = void>
63 class handler_work_base
64 {
65 public:
handler_work_base(int,int,const Executor & ex)66   explicit handler_work_base(int, int, const Executor& ex) BOOST_ASIO_NOEXCEPT
67     : executor_(boost::asio::prefer(ex, execution::outstanding_work.tracked))
68   {
69   }
70 
71   template <typename OtherExecutor>
handler_work_base(const Executor & ex,const OtherExecutor &)72   handler_work_base(const Executor& ex,
73       const OtherExecutor&) BOOST_ASIO_NOEXCEPT
74     : executor_(boost::asio::prefer(ex, execution::outstanding_work.tracked))
75   {
76   }
77 
handler_work_base(const handler_work_base & other)78   handler_work_base(const handler_work_base& other) BOOST_ASIO_NOEXCEPT
79     : executor_(other.executor_)
80   {
81   }
82 
83 #if defined(BOOST_ASIO_HAS_MOVE)
handler_work_base(handler_work_base && other)84   handler_work_base(handler_work_base&& other) BOOST_ASIO_NOEXCEPT
85     : executor_(BOOST_ASIO_MOVE_CAST(executor_type)(other.executor_))
86   {
87   }
88 #endif // defined(BOOST_ASIO_HAS_MOVE)
89 
owns_work() const90   bool owns_work() const BOOST_ASIO_NOEXCEPT
91   {
92     return true;
93   }
94 
95   template <typename Function, typename Handler>
dispatch(Function & function,Handler & handler)96   void dispatch(Function& function, Handler& handler)
97   {
98     execution::execute(
99         boost::asio::prefer(executor_,
100           execution::blocking.possibly,
101           execution::allocator((get_associated_allocator)(handler))),
102         BOOST_ASIO_MOVE_CAST(Function)(function));
103   }
104 
105 private:
106   typedef typename decay<
107       typename prefer_result<Executor,
108         execution::outstanding_work_t::tracked_t
109       >::type
110     >::type executor_type;
111 
112   executor_type executor_;
113 };
114 
115 template <typename Executor, typename CandidateExecutor,
116     typename IoContext, typename PolymorphicExecutor>
117 class handler_work_base<Executor, CandidateExecutor,
118     IoContext, PolymorphicExecutor,
119     typename enable_if<
120       !execution::is_executor<Executor>::value
121         && (!is_same<Executor, PolymorphicExecutor>::value
122           || !is_same<CandidateExecutor, void>::value)
123     >::type>
124 {
125 public:
handler_work_base(int,int,const Executor & ex)126   explicit handler_work_base(int, int, const Executor& ex) BOOST_ASIO_NOEXCEPT
127     : executor_(ex),
128       owns_work_(true)
129   {
130     executor_.on_work_started();
131   }
132 
handler_work_base(const Executor & ex,const Executor & candidate)133   handler_work_base(const Executor& ex,
134       const Executor& candidate) BOOST_ASIO_NOEXCEPT
135     : executor_(ex),
136       owns_work_(ex != candidate)
137   {
138     if (owns_work_)
139       executor_.on_work_started();
140   }
141 
142   template <typename OtherExecutor>
handler_work_base(const Executor & ex,const OtherExecutor &)143   handler_work_base(const Executor& ex,
144       const OtherExecutor&) BOOST_ASIO_NOEXCEPT
145     : executor_(ex),
146       owns_work_(true)
147   {
148     executor_.on_work_started();
149   }
150 
handler_work_base(const handler_work_base & other)151   handler_work_base(const handler_work_base& other) BOOST_ASIO_NOEXCEPT
152     : executor_(other.executor_),
153       owns_work_(other.owns_work_)
154   {
155     if (owns_work_)
156       executor_.on_work_started();
157   }
158 
159 #if defined(BOOST_ASIO_HAS_MOVE)
handler_work_base(handler_work_base && other)160   handler_work_base(handler_work_base&& other) BOOST_ASIO_NOEXCEPT
161     : executor_(BOOST_ASIO_MOVE_CAST(Executor)(other.executor_)),
162       owns_work_(other.owns_work_)
163   {
164     other.owns_work_ = false;
165   }
166 #endif // defined(BOOST_ASIO_HAS_MOVE)
167 
~handler_work_base()168   ~handler_work_base()
169   {
170     if (owns_work_)
171       executor_.on_work_finished();
172   }
173 
owns_work() const174   bool owns_work() const BOOST_ASIO_NOEXCEPT
175   {
176     return owns_work_;
177   }
178 
179   template <typename Function, typename Handler>
dispatch(Function & function,Handler & handler)180   void dispatch(Function& function, Handler& handler)
181   {
182     executor_.dispatch(BOOST_ASIO_MOVE_CAST(Function)(function),
183         boost::asio::get_associated_allocator(handler));
184   }
185 
186 private:
187   Executor executor_;
188   bool owns_work_;
189 };
190 
191 template <typename Executor, typename IoContext, typename PolymorphicExecutor>
192 class handler_work_base<Executor, void, IoContext, PolymorphicExecutor,
193     typename enable_if<
194       is_same<
195         Executor,
196         typename IoContext::executor_type
197       >::value
198     >::type>
199 {
200 public:
handler_work_base(int,int,const Executor &)201   explicit handler_work_base(int, int, const Executor&)
202   {
203   }
204 
owns_work() const205   bool owns_work() const BOOST_ASIO_NOEXCEPT
206   {
207     return false;
208   }
209 
210   template <typename Function, typename Handler>
dispatch(Function & function,Handler & handler)211   void dispatch(Function& function, Handler& handler)
212   {
213     // When using a native implementation, I/O completion handlers are
214     // already dispatched according to the execution context's executor's
215     // rules. We can call the function directly.
216     boost_asio_handler_invoke_helpers::invoke(function, handler);
217   }
218 };
219 
220 template <typename Executor, typename IoContext>
221 class handler_work_base<Executor, void, IoContext, Executor>
222 {
223 public:
handler_work_base(int,int,const Executor & ex)224   explicit handler_work_base(int, int, const Executor& ex) BOOST_ASIO_NOEXCEPT
225 #if !defined(BOOST_ASIO_NO_TYPEID)
226     : executor_(
227         ex.target_type() == typeid(typename IoContext::executor_type)
228           ? Executor() : ex)
229 #else // !defined(BOOST_ASIO_NO_TYPEID)
230     : executor_(ex)
231 #endif // !defined(BOOST_ASIO_NO_TYPEID)
232   {
233     if (executor_)
234       executor_.on_work_started();
235   }
236 
handler_work_base(const Executor & ex,const Executor & candidate)237   handler_work_base(const Executor& ex,
238       const Executor& candidate) BOOST_ASIO_NOEXCEPT
239     : executor_(ex != candidate ? ex : Executor())
240   {
241     if (executor_)
242       executor_.on_work_started();
243   }
244 
245   template <typename OtherExecutor>
handler_work_base(const Executor & ex,const OtherExecutor &)246   handler_work_base(const Executor& ex,
247       const OtherExecutor&) BOOST_ASIO_NOEXCEPT
248     : executor_(ex)
249   {
250     executor_.on_work_started();
251   }
252 
handler_work_base(const handler_work_base & other)253   handler_work_base(const handler_work_base& other) BOOST_ASIO_NOEXCEPT
254     : executor_(other.executor_)
255   {
256     if (executor_)
257       executor_.on_work_started();
258   }
259 
260 #if defined(BOOST_ASIO_HAS_MOVE)
handler_work_base(handler_work_base && other)261   handler_work_base(handler_work_base&& other) BOOST_ASIO_NOEXCEPT
262     : executor_(BOOST_ASIO_MOVE_CAST(Executor)(other.executor_))
263   {
264   }
265 #endif // defined(BOOST_ASIO_HAS_MOVE)
266 
~handler_work_base()267   ~handler_work_base()
268   {
269     if (executor_)
270       executor_.on_work_finished();
271   }
272 
owns_work() const273   bool owns_work() const BOOST_ASIO_NOEXCEPT
274   {
275     return !!executor_;
276   }
277 
278   template <typename Function, typename Handler>
dispatch(Function & function,Handler & handler)279   void dispatch(Function& function, Handler& handler)
280   {
281     executor_.dispatch(BOOST_ASIO_MOVE_CAST(Function)(function),
282         boost::asio::get_associated_allocator(handler));
283   }
284 
285 private:
286   Executor executor_;
287 };
288 
289 template <
290 #if defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
291     typename... SupportableProperties,
292 #else // defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
293     typename T1, typename T2, typename T3, typename T4, typename T5,
294     typename T6, typename T7, typename T8, typename T9,
295 #endif // defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
296     typename IoContext, typename PolymorphicExecutor>
297 class handler_work_base<
298 #if defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
299     execution::any_executor<SupportableProperties...>,
300 #else // defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
301     execution::any_executor<T1, T2, T3, T4, T5, T6, T7, T8, T9>,
302 #endif // defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
303     void, IoContext, PolymorphicExecutor>
304 {
305 public:
306   typedef
307 #if defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
308     execution::any_executor<SupportableProperties...>
309 #else // defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
310     execution::any_executor<T1, T2, T3, T4, T5, T6, T7, T8, T9>
311 #endif // defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
312     executor_type;
313 
handler_work_base(int,int,const executor_type & ex)314   explicit handler_work_base(int, int,
315       const executor_type& ex) BOOST_ASIO_NOEXCEPT
316 #if !defined(BOOST_ASIO_NO_TYPEID)
317     : executor_(
318         ex.target_type() == typeid(typename IoContext::executor_type)
319           ? executor_type()
320           : boost::asio::prefer(ex, execution::outstanding_work.tracked))
321 #else // !defined(BOOST_ASIO_NO_TYPEID)
322     : executor_(boost::asio::prefer(ex, execution::outstanding_work.tracked))
323 #endif // !defined(BOOST_ASIO_NO_TYPEID)
324   {
325   }
326 
handler_work_base(const executor_type & ex,const executor_type & candidate)327   handler_work_base(const executor_type& ex,
328       const executor_type& candidate) BOOST_ASIO_NOEXCEPT
329     : executor_(ex != candidate ? ex : executor_type())
330   {
331   }
332 
333   template <typename OtherExecutor>
handler_work_base(const executor_type & ex,const OtherExecutor &)334   handler_work_base(const executor_type& ex,
335       const OtherExecutor&) BOOST_ASIO_NOEXCEPT
336     : executor_(boost::asio::prefer(ex, execution::outstanding_work.tracked))
337   {
338   }
339 
handler_work_base(const handler_work_base & other)340   handler_work_base(const handler_work_base& other) BOOST_ASIO_NOEXCEPT
341     : executor_(other.executor_)
342   {
343   }
344 
345 #if defined(BOOST_ASIO_HAS_MOVE)
handler_work_base(handler_work_base && other)346   handler_work_base(handler_work_base&& other) BOOST_ASIO_NOEXCEPT
347     : executor_(BOOST_ASIO_MOVE_CAST(executor_type)(other.executor_))
348   {
349   }
350 #endif // defined(BOOST_ASIO_HAS_MOVE)
351 
owns_work() const352   bool owns_work() const BOOST_ASIO_NOEXCEPT
353   {
354     return !!executor_;
355   }
356 
357   template <typename Function, typename Handler>
dispatch(Function & function,Handler &)358   void dispatch(Function& function, Handler&)
359   {
360     execution::execute(
361         boost::asio::prefer(executor_, execution::blocking.possibly),
362         BOOST_ASIO_MOVE_CAST(Function)(function));
363   }
364 
365 private:
366   executor_type executor_;
367 };
368 
369 #if !defined(BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT)
370 
371 template <typename Executor, typename IoContext, typename PolymorphicExecutor>
372 class handler_work_base<Executor, void, IoContext, PolymorphicExecutor,
373     typename enable_if<
374       is_same<
375         Executor,
376         any_io_executor
377       >::value
378     >::type>
379 {
380 public:
381   typedef Executor executor_type;
382 
handler_work_base(int,int,const executor_type & ex)383   explicit handler_work_base(int, int,
384       const executor_type& ex) BOOST_ASIO_NOEXCEPT
385 #if !defined(BOOST_ASIO_NO_TYPEID)
386     : executor_(
387         ex.target_type() == typeid(typename IoContext::executor_type)
388           ? executor_type()
389           : boost::asio::prefer(ex, execution::outstanding_work.tracked))
390 #else // !defined(BOOST_ASIO_NO_TYPEID)
391     : executor_(boost::asio::prefer(ex, execution::outstanding_work.tracked))
392 #endif // !defined(BOOST_ASIO_NO_TYPEID)
393   {
394   }
395 
handler_work_base(const executor_type & ex,const executor_type & candidate)396   handler_work_base(const executor_type& ex,
397       const executor_type& candidate) BOOST_ASIO_NOEXCEPT
398     : executor_(ex != candidate ? ex : executor_type())
399   {
400   }
401 
402   template <typename OtherExecutor>
handler_work_base(const executor_type & ex,const OtherExecutor &)403   handler_work_base(const executor_type& ex,
404       const OtherExecutor&) BOOST_ASIO_NOEXCEPT
405     : executor_(boost::asio::prefer(ex, execution::outstanding_work.tracked))
406   {
407   }
408 
handler_work_base(const handler_work_base & other)409   handler_work_base(const handler_work_base& other) BOOST_ASIO_NOEXCEPT
410     : executor_(other.executor_)
411   {
412   }
413 
414 #if defined(BOOST_ASIO_HAS_MOVE)
handler_work_base(handler_work_base && other)415   handler_work_base(handler_work_base&& other) BOOST_ASIO_NOEXCEPT
416     : executor_(BOOST_ASIO_MOVE_CAST(executor_type)(other.executor_))
417   {
418   }
419 #endif // defined(BOOST_ASIO_HAS_MOVE)
420 
owns_work() const421   bool owns_work() const BOOST_ASIO_NOEXCEPT
422   {
423     return !!executor_;
424   }
425 
426   template <typename Function, typename Handler>
dispatch(Function & function,Handler &)427   void dispatch(Function& function, Handler&)
428   {
429     execution::execute(
430         boost::asio::prefer(executor_, execution::blocking.possibly),
431         BOOST_ASIO_MOVE_CAST(Function)(function));
432   }
433 
434 private:
435   executor_type executor_;
436 };
437 
438 #endif // !defined(BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT)
439 
440 template <typename Handler, typename IoExecutor, typename = void>
441 class handler_work :
442   handler_work_base<IoExecutor>,
443   handler_work_base<typename associated_executor<
444       Handler, IoExecutor>::type, IoExecutor>
445 {
446 public:
447   typedef handler_work_base<IoExecutor> base1_type;
448   typedef handler_work_base<typename associated_executor<
449     Handler, IoExecutor>::type, IoExecutor> base2_type;
450 
handler_work(Handler & handler,const IoExecutor & io_ex)451   handler_work(Handler& handler, const IoExecutor& io_ex) BOOST_ASIO_NOEXCEPT
452     : base1_type(0, 0, io_ex),
453       base2_type(boost::asio::get_associated_executor(handler, io_ex), io_ex)
454   {
455   }
456 
457   template <typename Function>
complete(Function & function,Handler & handler)458   void complete(Function& function, Handler& handler)
459   {
460     if (!base1_type::owns_work() && !base2_type::owns_work())
461     {
462       // When using a native implementation, I/O completion handlers are
463       // already dispatched according to the execution context's executor's
464       // rules. We can call the function directly.
465       boost_asio_handler_invoke_helpers::invoke(function, handler);
466     }
467     else
468     {
469       base2_type::dispatch(function, handler);
470     }
471   }
472 };
473 
474 template <typename Handler, typename IoExecutor>
475 class handler_work<
476     Handler, IoExecutor,
477     typename enable_if<
478       is_same<
479         typename associated_executor<Handler,
480           IoExecutor>::asio_associated_executor_is_unspecialised,
481         void
482       >::value
483     >::type> : handler_work_base<IoExecutor>
484 {
485 public:
486   typedef handler_work_base<IoExecutor> base1_type;
487 
handler_work(Handler &,const IoExecutor & io_ex)488   handler_work(Handler&, const IoExecutor& io_ex) BOOST_ASIO_NOEXCEPT
489     : base1_type(0, 0, io_ex)
490   {
491   }
492 
493   template <typename Function>
complete(Function & function,Handler & handler)494   void complete(Function& function, Handler& handler)
495   {
496     if (!base1_type::owns_work())
497     {
498       // When using a native implementation, I/O completion handlers are
499       // already dispatched according to the execution context's executor's
500       // rules. We can call the function directly.
501       boost_asio_handler_invoke_helpers::invoke(function, handler);
502     }
503     else
504     {
505       base1_type::dispatch(function, handler);
506     }
507   }
508 };
509 
510 } // namespace detail
511 } // namespace asio
512 } // namespace boost
513 
514 #include <boost/asio/detail/pop_options.hpp>
515 
516 #endif // BOOST_ASIO_DETAIL_HANDLER_WORK_HPP
517