1 //
2 // impl/spawn.hpp
3 // ~~~~~~~~~~~~~~
4 //
5 // Copyright (c) 2003-2019 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_IMPL_SPAWN_HPP
12 #define BOOST_ASIO_IMPL_SPAWN_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_allocator.hpp>
20 #include <boost/asio/associated_executor.hpp>
21 #include <boost/asio/async_result.hpp>
22 #include <boost/asio/bind_executor.hpp>
23 #include <boost/asio/detail/atomic_count.hpp>
24 #include <boost/asio/detail/handler_alloc_helpers.hpp>
25 #include <boost/asio/detail/handler_cont_helpers.hpp>
26 #include <boost/asio/detail/handler_invoke_helpers.hpp>
27 #include <boost/asio/detail/memory.hpp>
28 #include <boost/asio/detail/noncopyable.hpp>
29 #include <boost/asio/detail/type_traits.hpp>
30 #include <boost/system/system_error.hpp>
31 
32 #include <boost/asio/detail/push_options.hpp>
33 
34 namespace boost {
35 namespace asio {
36 namespace detail {
37 
38   template <typename Handler, typename T>
39   class coro_handler
40   {
41   public:
coro_handler(basic_yield_context<Handler> ctx)42     coro_handler(basic_yield_context<Handler> ctx)
43       : coro_(ctx.coro_.lock()),
44         ca_(ctx.ca_),
45         handler_(ctx.handler_),
46         ready_(0),
47         ec_(ctx.ec_),
48         value_(0)
49     {
50     }
51 
operator ()(T value)52     void operator()(T value)
53     {
54       *ec_ = boost::system::error_code();
55       *value_ = BOOST_ASIO_MOVE_CAST(T)(value);
56       if (--*ready_ == 0)
57         (*coro_)();
58     }
59 
operator ()(boost::system::error_code ec,T value)60     void operator()(boost::system::error_code ec, T value)
61     {
62       *ec_ = ec;
63       *value_ = BOOST_ASIO_MOVE_CAST(T)(value);
64       if (--*ready_ == 0)
65         (*coro_)();
66     }
67 
68   //private:
69     shared_ptr<typename basic_yield_context<Handler>::callee_type> coro_;
70     typename basic_yield_context<Handler>::caller_type& ca_;
71     Handler handler_;
72     atomic_count* ready_;
73     boost::system::error_code* ec_;
74     T* value_;
75   };
76 
77   template <typename Handler>
78   class coro_handler<Handler, void>
79   {
80   public:
coro_handler(basic_yield_context<Handler> ctx)81     coro_handler(basic_yield_context<Handler> ctx)
82       : coro_(ctx.coro_.lock()),
83         ca_(ctx.ca_),
84         handler_(ctx.handler_),
85         ready_(0),
86         ec_(ctx.ec_)
87     {
88     }
89 
operator ()()90     void operator()()
91     {
92       *ec_ = boost::system::error_code();
93       if (--*ready_ == 0)
94         (*coro_)();
95     }
96 
operator ()(boost::system::error_code ec)97     void operator()(boost::system::error_code ec)
98     {
99       *ec_ = ec;
100       if (--*ready_ == 0)
101         (*coro_)();
102     }
103 
104   //private:
105     shared_ptr<typename basic_yield_context<Handler>::callee_type> coro_;
106     typename basic_yield_context<Handler>::caller_type& ca_;
107     Handler handler_;
108     atomic_count* ready_;
109     boost::system::error_code* ec_;
110   };
111 
112   template <typename Handler, typename T>
asio_handler_allocate(std::size_t size,coro_handler<Handler,T> * this_handler)113   inline void* asio_handler_allocate(std::size_t size,
114       coro_handler<Handler, T>* this_handler)
115   {
116     return boost_asio_handler_alloc_helpers::allocate(
117         size, this_handler->handler_);
118   }
119 
120   template <typename Handler, typename T>
asio_handler_deallocate(void * pointer,std::size_t size,coro_handler<Handler,T> * this_handler)121   inline void asio_handler_deallocate(void* pointer, std::size_t size,
122       coro_handler<Handler, T>* this_handler)
123   {
124     boost_asio_handler_alloc_helpers::deallocate(
125         pointer, size, this_handler->handler_);
126   }
127 
128   template <typename Handler, typename T>
asio_handler_is_continuation(coro_handler<Handler,T> *)129   inline bool asio_handler_is_continuation(coro_handler<Handler, T>*)
130   {
131     return true;
132   }
133 
134   template <typename Function, typename Handler, typename T>
asio_handler_invoke(Function & function,coro_handler<Handler,T> * this_handler)135   inline void asio_handler_invoke(Function& function,
136       coro_handler<Handler, T>* this_handler)
137   {
138     boost_asio_handler_invoke_helpers::invoke(
139         function, this_handler->handler_);
140   }
141 
142   template <typename Function, typename Handler, typename T>
asio_handler_invoke(const Function & function,coro_handler<Handler,T> * this_handler)143   inline void asio_handler_invoke(const Function& function,
144       coro_handler<Handler, T>* this_handler)
145   {
146     boost_asio_handler_invoke_helpers::invoke(
147         function, this_handler->handler_);
148   }
149 
150   template <typename Handler, typename T>
151   class coro_async_result
152   {
153   public:
154     typedef coro_handler<Handler, T> completion_handler_type;
155     typedef T return_type;
156 
coro_async_result(completion_handler_type & h)157     explicit coro_async_result(completion_handler_type& h)
158       : handler_(h),
159         ca_(h.ca_),
160         ready_(2)
161     {
162       h.ready_ = &ready_;
163       out_ec_ = h.ec_;
164       if (!out_ec_) h.ec_ = &ec_;
165       h.value_ = &value_;
166     }
167 
get()168     return_type get()
169     {
170       // Must not hold shared_ptr to coro while suspended.
171       handler_.coro_.reset();
172 
173       if (--ready_ != 0)
174         ca_();
175       if (!out_ec_ && ec_) throw boost::system::system_error(ec_);
176       return BOOST_ASIO_MOVE_CAST(return_type)(value_);
177     }
178 
179   private:
180     completion_handler_type& handler_;
181     typename basic_yield_context<Handler>::caller_type& ca_;
182     atomic_count ready_;
183     boost::system::error_code* out_ec_;
184     boost::system::error_code ec_;
185     return_type value_;
186   };
187 
188   template <typename Handler>
189   class coro_async_result<Handler, void>
190   {
191   public:
192     typedef coro_handler<Handler, void> completion_handler_type;
193     typedef void return_type;
194 
coro_async_result(completion_handler_type & h)195     explicit coro_async_result(completion_handler_type& h)
196       : handler_(h),
197         ca_(h.ca_),
198         ready_(2)
199     {
200       h.ready_ = &ready_;
201       out_ec_ = h.ec_;
202       if (!out_ec_) h.ec_ = &ec_;
203     }
204 
get()205     void get()
206     {
207       // Must not hold shared_ptr to coro while suspended.
208       handler_.coro_.reset();
209 
210       if (--ready_ != 0)
211         ca_();
212       if (!out_ec_ && ec_) throw boost::system::system_error(ec_);
213     }
214 
215   private:
216     completion_handler_type& handler_;
217     typename basic_yield_context<Handler>::caller_type& ca_;
218     atomic_count ready_;
219     boost::system::error_code* out_ec_;
220     boost::system::error_code ec_;
221   };
222 
223 } // namespace detail
224 
225 #if !defined(GENERATING_DOCUMENTATION)
226 
227 template <typename Handler, typename ReturnType>
228 class async_result<basic_yield_context<Handler>, ReturnType()>
229   : public detail::coro_async_result<Handler, void>
230 {
231 public:
async_result(typename detail::coro_async_result<Handler,void>::completion_handler_type & h)232   explicit async_result(
233     typename detail::coro_async_result<Handler,
234       void>::completion_handler_type& h)
235     : detail::coro_async_result<Handler, void>(h)
236   {
237   }
238 };
239 
240 template <typename Handler, typename ReturnType, typename Arg1>
241 class async_result<basic_yield_context<Handler>, ReturnType(Arg1)>
242   : public detail::coro_async_result<Handler, typename decay<Arg1>::type>
243 {
244 public:
async_result(typename detail::coro_async_result<Handler,typename decay<Arg1>::type>::completion_handler_type & h)245   explicit async_result(
246     typename detail::coro_async_result<Handler,
247       typename decay<Arg1>::type>::completion_handler_type& h)
248     : detail::coro_async_result<Handler, typename decay<Arg1>::type>(h)
249   {
250   }
251 };
252 
253 template <typename Handler, typename ReturnType>
254 class async_result<basic_yield_context<Handler>,
255     ReturnType(boost::system::error_code)>
256   : public detail::coro_async_result<Handler, void>
257 {
258 public:
async_result(typename detail::coro_async_result<Handler,void>::completion_handler_type & h)259   explicit async_result(
260     typename detail::coro_async_result<Handler,
261       void>::completion_handler_type& h)
262     : detail::coro_async_result<Handler, void>(h)
263   {
264   }
265 };
266 
267 template <typename Handler, typename ReturnType, typename Arg2>
268 class async_result<basic_yield_context<Handler>,
269     ReturnType(boost::system::error_code, Arg2)>
270   : public detail::coro_async_result<Handler, typename decay<Arg2>::type>
271 {
272 public:
async_result(typename detail::coro_async_result<Handler,typename decay<Arg2>::type>::completion_handler_type & h)273   explicit async_result(
274     typename detail::coro_async_result<Handler,
275       typename decay<Arg2>::type>::completion_handler_type& h)
276     : detail::coro_async_result<Handler, typename decay<Arg2>::type>(h)
277   {
278   }
279 };
280 
281 template <typename Handler, typename T, typename Allocator>
282 struct associated_allocator<detail::coro_handler<Handler, T>, Allocator>
283 {
284   typedef typename associated_allocator<Handler, Allocator>::type type;
285 
getboost::asio::associated_allocator286   static type get(const detail::coro_handler<Handler, T>& h,
287       const Allocator& a = Allocator()) BOOST_ASIO_NOEXCEPT
288   {
289     return associated_allocator<Handler, Allocator>::get(h.handler_, a);
290   }
291 };
292 
293 template <typename Handler, typename T, typename Executor>
294 struct associated_executor<detail::coro_handler<Handler, T>, Executor>
295 {
296   typedef typename associated_executor<Handler, Executor>::type type;
297 
getboost::asio::associated_executor298   static type get(const detail::coro_handler<Handler, T>& h,
299       const Executor& ex = Executor()) BOOST_ASIO_NOEXCEPT
300   {
301     return associated_executor<Handler, Executor>::get(h.handler_, ex);
302   }
303 };
304 
305 namespace detail {
306 
307   template <typename Handler, typename Function>
308   struct spawn_data : private noncopyable
309   {
310     template <typename Hand, typename Func>
spawn_databoost::asio::detail::spawn_data311     spawn_data(BOOST_ASIO_MOVE_ARG(Hand) handler,
312         bool call_handler, BOOST_ASIO_MOVE_ARG(Func) function)
313       : handler_(BOOST_ASIO_MOVE_CAST(Hand)(handler)),
314         call_handler_(call_handler),
315         function_(BOOST_ASIO_MOVE_CAST(Func)(function))
316     {
317     }
318 
319     weak_ptr<typename basic_yield_context<Handler>::callee_type> coro_;
320     Handler handler_;
321     bool call_handler_;
322     Function function_;
323   };
324 
325   template <typename Handler, typename Function>
326   struct coro_entry_point
327   {
operator ()boost::asio::detail::coro_entry_point328     void operator()(typename basic_yield_context<Handler>::caller_type& ca)
329     {
330       shared_ptr<spawn_data<Handler, Function> > data(data_);
331 #if !defined(BOOST_COROUTINES_UNIDIRECT) && !defined(BOOST_COROUTINES_V2)
332       ca(); // Yield until coroutine pointer has been initialised.
333 #endif // !defined(BOOST_COROUTINES_UNIDIRECT) && !defined(BOOST_COROUTINES_V2)
334       const basic_yield_context<Handler> yield(
335           data->coro_, ca, data->handler_);
336 
337       (data->function_)(yield);
338       if (data->call_handler_)
339         (data->handler_)();
340     }
341 
342     shared_ptr<spawn_data<Handler, Function> > data_;
343   };
344 
345   template <typename Handler, typename Function>
346   struct spawn_helper
347   {
operator ()boost::asio::detail::spawn_helper348     void operator()()
349     {
350       typedef typename basic_yield_context<Handler>::callee_type callee_type;
351       coro_entry_point<Handler, Function> entry_point = { data_ };
352       shared_ptr<callee_type> coro(new callee_type(entry_point, attributes_));
353       data_->coro_ = coro;
354       (*coro)();
355     }
356 
357     shared_ptr<spawn_data<Handler, Function> > data_;
358     boost::coroutines::attributes attributes_;
359   };
360 
361   template <typename Function, typename Handler, typename Function1>
asio_handler_invoke(Function & function,spawn_helper<Handler,Function1> * this_handler)362   inline void asio_handler_invoke(Function& function,
363       spawn_helper<Handler, Function1>* this_handler)
364   {
365     boost_asio_handler_invoke_helpers::invoke(
366         function, this_handler->data_->handler_);
367   }
368 
369   template <typename Function, typename Handler, typename Function1>
asio_handler_invoke(const Function & function,spawn_helper<Handler,Function1> * this_handler)370   inline void asio_handler_invoke(const Function& function,
371       spawn_helper<Handler, Function1>* this_handler)
372   {
373     boost_asio_handler_invoke_helpers::invoke(
374         function, this_handler->data_->handler_);
375   }
376 
default_spawn_handler()377   inline void default_spawn_handler() {}
378 
379 } // namespace detail
380 
381 template <typename Function>
spawn(BOOST_ASIO_MOVE_ARG (Function)function,const boost::coroutines::attributes & attributes)382 inline void spawn(BOOST_ASIO_MOVE_ARG(Function) function,
383     const boost::coroutines::attributes& attributes)
384 {
385   typedef typename decay<Function>::type function_type;
386 
387   typename associated_executor<function_type>::type ex(
388       (get_associated_executor)(function));
389 
390   boost::asio::spawn(ex, BOOST_ASIO_MOVE_CAST(Function)(function), attributes);
391 }
392 
393 template <typename Handler, typename Function>
spawn(BOOST_ASIO_MOVE_ARG (Handler)handler,BOOST_ASIO_MOVE_ARG (Function)function,const boost::coroutines::attributes & attributes,typename enable_if<!is_executor<typename decay<Handler>::type>::value &&!is_convertible<Handler &,execution_context &>::value>::type *)394 void spawn(BOOST_ASIO_MOVE_ARG(Handler) handler,
395     BOOST_ASIO_MOVE_ARG(Function) function,
396     const boost::coroutines::attributes& attributes,
397     typename enable_if<!is_executor<typename decay<Handler>::type>::value &&
398       !is_convertible<Handler&, execution_context&>::value>::type*)
399 {
400   typedef typename decay<Handler>::type handler_type;
401   typedef typename decay<Function>::type function_type;
402 
403   typename associated_executor<handler_type>::type ex(
404       (get_associated_executor)(handler));
405 
406   typename associated_allocator<handler_type>::type a(
407       (get_associated_allocator)(handler));
408 
409   detail::spawn_helper<handler_type, function_type> helper;
410   helper.data_.reset(
411       new detail::spawn_data<handler_type, function_type>(
412         BOOST_ASIO_MOVE_CAST(Handler)(handler), true,
413         BOOST_ASIO_MOVE_CAST(Function)(function)));
414   helper.attributes_ = attributes;
415 
416   ex.dispatch(helper, a);
417 }
418 
419 template <typename Handler, typename Function>
spawn(basic_yield_context<Handler> ctx,BOOST_ASIO_MOVE_ARG (Function)function,const boost::coroutines::attributes & attributes)420 void spawn(basic_yield_context<Handler> ctx,
421     BOOST_ASIO_MOVE_ARG(Function) function,
422     const boost::coroutines::attributes& attributes)
423 {
424   typedef typename decay<Function>::type function_type;
425 
426   Handler handler(ctx.handler_); // Explicit copy that might be moved from.
427 
428   typename associated_executor<Handler>::type ex(
429       (get_associated_executor)(handler));
430 
431   typename associated_allocator<Handler>::type a(
432       (get_associated_allocator)(handler));
433 
434   detail::spawn_helper<Handler, function_type> helper;
435   helper.data_.reset(
436       new detail::spawn_data<Handler, function_type>(
437         BOOST_ASIO_MOVE_CAST(Handler)(handler), false,
438         BOOST_ASIO_MOVE_CAST(Function)(function)));
439   helper.attributes_ = attributes;
440 
441   ex.dispatch(helper, a);
442 }
443 
444 template <typename Function, typename Executor>
spawn(const Executor & ex,BOOST_ASIO_MOVE_ARG (Function)function,const boost::coroutines::attributes & attributes,typename enable_if<is_executor<Executor>::value>::type *)445 inline void spawn(const Executor& ex,
446     BOOST_ASIO_MOVE_ARG(Function) function,
447     const boost::coroutines::attributes& attributes,
448     typename enable_if<is_executor<Executor>::value>::type*)
449 {
450   boost::asio::spawn(boost::asio::strand<Executor>(ex),
451       BOOST_ASIO_MOVE_CAST(Function)(function), attributes);
452 }
453 
454 template <typename Function, typename Executor>
spawn(const strand<Executor> & ex,BOOST_ASIO_MOVE_ARG (Function)function,const boost::coroutines::attributes & attributes)455 inline void spawn(const strand<Executor>& ex,
456     BOOST_ASIO_MOVE_ARG(Function) function,
457     const boost::coroutines::attributes& attributes)
458 {
459   boost::asio::spawn(boost::asio::bind_executor(
460         ex, &detail::default_spawn_handler),
461       BOOST_ASIO_MOVE_CAST(Function)(function), attributes);
462 }
463 
464 template <typename Function>
spawn(const boost::asio::io_context::strand & s,BOOST_ASIO_MOVE_ARG (Function)function,const boost::coroutines::attributes & attributes)465 inline void spawn(const boost::asio::io_context::strand& s,
466     BOOST_ASIO_MOVE_ARG(Function) function,
467     const boost::coroutines::attributes& attributes)
468 {
469   boost::asio::spawn(boost::asio::bind_executor(
470         s, &detail::default_spawn_handler),
471       BOOST_ASIO_MOVE_CAST(Function)(function), attributes);
472 }
473 
474 template <typename Function, typename ExecutionContext>
spawn(ExecutionContext & ctx,BOOST_ASIO_MOVE_ARG (Function)function,const boost::coroutines::attributes & attributes,typename enable_if<is_convertible<ExecutionContext &,execution_context &>::value>::type *)475 inline void spawn(ExecutionContext& ctx,
476     BOOST_ASIO_MOVE_ARG(Function) function,
477     const boost::coroutines::attributes& attributes,
478     typename enable_if<is_convertible<
479       ExecutionContext&, execution_context&>::value>::type*)
480 {
481   boost::asio::spawn(ctx.get_executor(),
482       BOOST_ASIO_MOVE_CAST(Function)(function), attributes);
483 }
484 
485 #endif // !defined(GENERATING_DOCUMENTATION)
486 
487 } // namespace asio
488 } // namespace boost
489 
490 #include <boost/asio/detail/pop_options.hpp>
491 
492 #endif // BOOST_ASIO_IMPL_SPAWN_HPP
493