Warning, file /include/boost/asio/impl/spawn.hpp was not indexed
or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #ifndef BOOST_ASIO_IMPL_SPAWN_HPP
0012 #define BOOST_ASIO_IMPL_SPAWN_HPP
0013
0014 #if defined(_MSC_VER) && (_MSC_VER >= 1200)
0015 # pragma once
0016 #endif
0017
0018 #include <boost/asio/detail/config.hpp>
0019 #include <tuple>
0020 #include <boost/asio/associated_allocator.hpp>
0021 #include <boost/asio/associated_cancellation_slot.hpp>
0022 #include <boost/asio/associated_executor.hpp>
0023 #include <boost/asio/async_result.hpp>
0024 #include <boost/asio/bind_executor.hpp>
0025 #include <boost/asio/detail/atomic_count.hpp>
0026 #include <boost/asio/detail/bind_handler.hpp>
0027 #include <boost/asio/detail/handler_cont_helpers.hpp>
0028 #include <boost/asio/detail/memory.hpp>
0029 #include <boost/asio/detail/noncopyable.hpp>
0030 #include <boost/asio/detail/type_traits.hpp>
0031 #include <boost/asio/detail/utility.hpp>
0032 #include <boost/asio/error.hpp>
0033 #include <boost/system/system_error.hpp>
0034
0035 #if defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
0036 # include <boost/context/fiber.hpp>
0037 #endif
0038
0039 #include <boost/asio/detail/push_options.hpp>
0040
0041 namespace boost {
0042 namespace asio {
0043 namespace detail {
0044
0045 #if !defined(BOOST_ASIO_NO_EXCEPTIONS)
0046 inline void spawned_thread_rethrow(void* ex)
0047 {
0048 if (*static_cast<exception_ptr*>(ex))
0049 rethrow_exception(*static_cast<exception_ptr*>(ex));
0050 }
0051 #endif
0052
0053 #if defined(BOOST_ASIO_HAS_BOOST_COROUTINE)
0054
0055
0056 class spawned_coroutine_thread : public spawned_thread_base
0057 {
0058 public:
0059 #if defined(BOOST_COROUTINES_UNIDIRECT) || defined(BOOST_COROUTINES_V2)
0060 typedef boost::coroutines::pull_coroutine<void> callee_type;
0061 typedef boost::coroutines::push_coroutine<void> caller_type;
0062 #else
0063 typedef boost::coroutines::coroutine<void()> callee_type;
0064 typedef boost::coroutines::coroutine<void()> caller_type;
0065 #endif
0066
0067 spawned_coroutine_thread(caller_type& caller)
0068 : caller_(caller),
0069 on_suspend_fn_(0),
0070 on_suspend_arg_(0)
0071 {
0072 }
0073
0074 template <typename F>
0075 static spawned_thread_base* spawn(F&& f,
0076 const boost::coroutines::attributes& attributes,
0077 cancellation_slot parent_cancel_slot = cancellation_slot(),
0078 cancellation_state cancel_state = cancellation_state())
0079 {
0080 spawned_coroutine_thread* spawned_thread = 0;
0081 callee_type callee(entry_point<decay_t<F>>(
0082 static_cast<F&&>(f), &spawned_thread), attributes);
0083 spawned_thread->callee_.swap(callee);
0084 spawned_thread->parent_cancellation_slot_ = parent_cancel_slot;
0085 spawned_thread->cancellation_state_ = cancel_state;
0086 return spawned_thread;
0087 }
0088
0089 template <typename F>
0090 static spawned_thread_base* spawn(F&& f,
0091 cancellation_slot parent_cancel_slot = cancellation_slot(),
0092 cancellation_state cancel_state = cancellation_state())
0093 {
0094 return spawn(static_cast<F&&>(f), boost::coroutines::attributes(),
0095 parent_cancel_slot, cancel_state);
0096 }
0097
0098 void resume()
0099 {
0100 callee_();
0101 if (on_suspend_fn_)
0102 {
0103 void (*fn)(void*) = on_suspend_fn_;
0104 void* arg = on_suspend_arg_;
0105 on_suspend_fn_ = 0;
0106 fn(arg);
0107 }
0108 }
0109
0110 void suspend_with(void (*fn)(void*), void* arg)
0111 {
0112 if (throw_if_cancelled_)
0113 if (!!cancellation_state_.cancelled())
0114 throw_error(boost::asio::error::operation_aborted, "yield");
0115 has_context_switched_ = true;
0116 on_suspend_fn_ = fn;
0117 on_suspend_arg_ = arg;
0118 caller_();
0119 }
0120
0121 void destroy()
0122 {
0123 callee_type callee;
0124 callee.swap(callee_);
0125 if (terminal_)
0126 callee();
0127 }
0128
0129 private:
0130 template <typename Function>
0131 class entry_point
0132 {
0133 public:
0134 template <typename F>
0135 entry_point(F&& f,
0136 spawned_coroutine_thread** spawned_thread_out)
0137 : function_(static_cast<F&&>(f)),
0138 spawned_thread_out_(spawned_thread_out)
0139 {
0140 }
0141
0142 void operator()(caller_type& caller)
0143 {
0144 Function function(static_cast<Function&&>(function_));
0145 spawned_coroutine_thread spawned_thread(caller);
0146 *spawned_thread_out_ = &spawned_thread;
0147 spawned_thread_out_ = 0;
0148 spawned_thread.suspend();
0149 #if !defined(BOOST_ASIO_NO_EXCEPTIONS)
0150 try
0151 #endif
0152 {
0153 function(&spawned_thread);
0154 spawned_thread.terminal_ = true;
0155 spawned_thread.suspend();
0156 }
0157 #if !defined(BOOST_ASIO_NO_EXCEPTIONS)
0158 catch (const boost::coroutines::detail::forced_unwind&)
0159 {
0160 throw;
0161 }
0162 catch (...)
0163 {
0164 exception_ptr ex = current_exception();
0165 spawned_thread.terminal_ = true;
0166 spawned_thread.suspend_with(spawned_thread_rethrow, &ex);
0167 }
0168 #endif
0169 }
0170
0171 private:
0172 Function function_;
0173 spawned_coroutine_thread** spawned_thread_out_;
0174 };
0175
0176 caller_type& caller_;
0177 callee_type callee_;
0178 void (*on_suspend_fn_)(void*);
0179 void* on_suspend_arg_;
0180 };
0181
0182 #endif
0183
0184 #if defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
0185
0186
0187 class spawned_fiber_thread : public spawned_thread_base
0188 {
0189 public:
0190 typedef boost::context::fiber fiber_type;
0191
0192 spawned_fiber_thread(fiber_type&& caller)
0193 : caller_(static_cast<fiber_type&&>(caller)),
0194 on_suspend_fn_(0),
0195 on_suspend_arg_(0)
0196 {
0197 }
0198
0199 template <typename StackAllocator, typename F>
0200 static spawned_thread_base* spawn(allocator_arg_t,
0201 StackAllocator&& stack_allocator,
0202 F&& f,
0203 cancellation_slot parent_cancel_slot = cancellation_slot(),
0204 cancellation_state cancel_state = cancellation_state())
0205 {
0206 spawned_fiber_thread* spawned_thread = 0;
0207 fiber_type callee(allocator_arg_t(),
0208 static_cast<StackAllocator&&>(stack_allocator),
0209 entry_point<decay_t<F>>(
0210 static_cast<F&&>(f), &spawned_thread));
0211 callee = fiber_type(static_cast<fiber_type&&>(callee)).resume();
0212 spawned_thread->callee_ = static_cast<fiber_type&&>(callee);
0213 spawned_thread->parent_cancellation_slot_ = parent_cancel_slot;
0214 spawned_thread->cancellation_state_ = cancel_state;
0215 return spawned_thread;
0216 }
0217
0218 template <typename F>
0219 static spawned_thread_base* spawn(F&& f,
0220 cancellation_slot parent_cancel_slot = cancellation_slot(),
0221 cancellation_state cancel_state = cancellation_state())
0222 {
0223 return spawn(allocator_arg_t(), boost::context::fixedsize_stack(),
0224 static_cast<F&&>(f), parent_cancel_slot, cancel_state);
0225 }
0226
0227 void resume()
0228 {
0229 callee_ = fiber_type(static_cast<fiber_type&&>(callee_)).resume();
0230 if (on_suspend_fn_)
0231 {
0232 void (*fn)(void*) = on_suspend_fn_;
0233 void* arg = on_suspend_arg_;
0234 on_suspend_fn_ = 0;
0235 fn(arg);
0236 }
0237 }
0238
0239 void suspend_with(void (*fn)(void*), void* arg)
0240 {
0241 if (throw_if_cancelled_)
0242 if (!!cancellation_state_.cancelled())
0243 throw_error(boost::asio::error::operation_aborted, "yield");
0244 has_context_switched_ = true;
0245 on_suspend_fn_ = fn;
0246 on_suspend_arg_ = arg;
0247 caller_ = fiber_type(static_cast<fiber_type&&>(caller_)).resume();
0248 }
0249
0250 void destroy()
0251 {
0252 fiber_type callee = static_cast<fiber_type&&>(callee_);
0253 if (terminal_)
0254 fiber_type(static_cast<fiber_type&&>(callee)).resume();
0255 }
0256
0257 private:
0258 template <typename Function>
0259 class entry_point
0260 {
0261 public:
0262 template <typename F>
0263 entry_point(F&& f,
0264 spawned_fiber_thread** spawned_thread_out)
0265 : function_(static_cast<F&&>(f)),
0266 spawned_thread_out_(spawned_thread_out)
0267 {
0268 }
0269
0270 fiber_type operator()(fiber_type&& caller)
0271 {
0272 Function function(static_cast<Function&&>(function_));
0273 spawned_fiber_thread spawned_thread(
0274 static_cast<fiber_type&&>(caller));
0275 *spawned_thread_out_ = &spawned_thread;
0276 spawned_thread_out_ = 0;
0277 spawned_thread.suspend();
0278 #if !defined(BOOST_ASIO_NO_EXCEPTIONS)
0279 try
0280 #endif
0281 {
0282 function(&spawned_thread);
0283 spawned_thread.terminal_ = true;
0284 spawned_thread.suspend();
0285 }
0286 #if !defined(BOOST_ASIO_NO_EXCEPTIONS)
0287 catch (const boost::context::detail::forced_unwind&)
0288 {
0289 throw;
0290 }
0291 catch (...)
0292 {
0293 exception_ptr ex = current_exception();
0294 spawned_thread.terminal_ = true;
0295 spawned_thread.suspend_with(spawned_thread_rethrow, &ex);
0296 }
0297 #endif
0298 return static_cast<fiber_type&&>(spawned_thread.caller_);
0299 }
0300
0301 private:
0302 Function function_;
0303 spawned_fiber_thread** spawned_thread_out_;
0304 };
0305
0306 fiber_type caller_;
0307 fiber_type callee_;
0308 void (*on_suspend_fn_)(void*);
0309 void* on_suspend_arg_;
0310 };
0311
0312 #endif
0313
0314 #if defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
0315 typedef spawned_fiber_thread default_spawned_thread_type;
0316 #elif defined(BOOST_ASIO_HAS_BOOST_COROUTINE)
0317 typedef spawned_coroutine_thread default_spawned_thread_type;
0318 #else
0319 # error No spawn() implementation available
0320 #endif
0321
0322
0323 class spawned_thread_resumer
0324 {
0325 public:
0326 explicit spawned_thread_resumer(spawned_thread_base* spawned_thread)
0327 : spawned_thread_(spawned_thread)
0328 {
0329 }
0330
0331 spawned_thread_resumer(spawned_thread_resumer&& other) noexcept
0332 : spawned_thread_(other.spawned_thread_)
0333 {
0334 other.spawned_thread_ = 0;
0335 }
0336
0337 ~spawned_thread_resumer()
0338 {
0339 if (spawned_thread_)
0340 spawned_thread_->destroy();
0341 }
0342
0343 void operator()()
0344 {
0345 spawned_thread_->attach(&spawned_thread_);
0346 spawned_thread_->resume();
0347 }
0348
0349 private:
0350 spawned_thread_base* spawned_thread_;
0351 };
0352
0353
0354 class spawned_thread_destroyer
0355 {
0356 public:
0357 explicit spawned_thread_destroyer(spawned_thread_base* spawned_thread)
0358 : spawned_thread_(spawned_thread)
0359 {
0360 spawned_thread->detach();
0361 }
0362
0363 spawned_thread_destroyer(spawned_thread_destroyer&& other) noexcept
0364 : spawned_thread_(other.spawned_thread_)
0365 {
0366 other.spawned_thread_ = 0;
0367 }
0368
0369 ~spawned_thread_destroyer()
0370 {
0371 if (spawned_thread_)
0372 spawned_thread_->destroy();
0373 }
0374
0375 void operator()()
0376 {
0377 if (spawned_thread_)
0378 {
0379 spawned_thread_->destroy();
0380 spawned_thread_ = 0;
0381 }
0382 }
0383
0384 private:
0385 spawned_thread_base* spawned_thread_;
0386 };
0387
0388
0389 template <typename Executor>
0390 class spawn_handler_base
0391 {
0392 public:
0393 typedef Executor executor_type;
0394 typedef cancellation_slot cancellation_slot_type;
0395
0396 spawn_handler_base(const basic_yield_context<Executor>& yield)
0397 : yield_(yield),
0398 spawned_thread_(yield.spawned_thread_)
0399 {
0400 spawned_thread_->detach();
0401 }
0402
0403 spawn_handler_base(spawn_handler_base&& other) noexcept
0404 : yield_(other.yield_),
0405 spawned_thread_(other.spawned_thread_)
0406
0407 {
0408 other.spawned_thread_ = 0;
0409 }
0410
0411 ~spawn_handler_base()
0412 {
0413 if (spawned_thread_)
0414 (post)(yield_.executor_, spawned_thread_destroyer(spawned_thread_));
0415 }
0416
0417 executor_type get_executor() const noexcept
0418 {
0419 return yield_.executor_;
0420 }
0421
0422 cancellation_slot_type get_cancellation_slot() const noexcept
0423 {
0424 return spawned_thread_->get_cancellation_slot();
0425 }
0426
0427 void resume()
0428 {
0429 spawned_thread_resumer resumer(spawned_thread_);
0430 spawned_thread_ = 0;
0431 resumer();
0432 }
0433
0434 protected:
0435 const basic_yield_context<Executor>& yield_;
0436 spawned_thread_base* spawned_thread_;
0437 };
0438
0439
0440 template <typename Executor, typename Signature>
0441 class spawn_handler;
0442
0443 template <typename Executor, typename R>
0444 class spawn_handler<Executor, R()>
0445 : public spawn_handler_base<Executor>
0446 {
0447 public:
0448 typedef void return_type;
0449
0450 struct result_type {};
0451
0452 spawn_handler(const basic_yield_context<Executor>& yield, result_type&)
0453 : spawn_handler_base<Executor>(yield)
0454 {
0455 }
0456
0457 void operator()()
0458 {
0459 this->resume();
0460 }
0461
0462 static return_type on_resume(result_type&)
0463 {
0464 }
0465 };
0466
0467 template <typename Executor, typename R>
0468 class spawn_handler<Executor, R(boost::system::error_code)>
0469 : public spawn_handler_base<Executor>
0470 {
0471 public:
0472 typedef void return_type;
0473 typedef boost::system::error_code* result_type;
0474
0475 spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
0476 : spawn_handler_base<Executor>(yield),
0477 result_(result)
0478 {
0479 }
0480
0481 void operator()(boost::system::error_code ec)
0482 {
0483 if (this->yield_.ec_)
0484 {
0485 *this->yield_.ec_ = ec;
0486 result_ = 0;
0487 }
0488 else
0489 result_ = &ec;
0490 this->resume();
0491 }
0492
0493 static return_type on_resume(result_type& result)
0494 {
0495 if (result)
0496 throw_error(*result);
0497 }
0498
0499 private:
0500 result_type& result_;
0501 };
0502
0503 template <typename Executor, typename R>
0504 class spawn_handler<Executor, R(exception_ptr)>
0505 : public spawn_handler_base<Executor>
0506 {
0507 public:
0508 typedef void return_type;
0509 typedef exception_ptr* result_type;
0510
0511 spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
0512 : spawn_handler_base<Executor>(yield),
0513 result_(result)
0514 {
0515 }
0516
0517 void operator()(exception_ptr ex)
0518 {
0519 result_ = &ex;
0520 this->resume();
0521 }
0522
0523 static return_type on_resume(result_type& result)
0524 {
0525 if (*result)
0526 rethrow_exception(*result);
0527 }
0528
0529 private:
0530 result_type& result_;
0531 };
0532
0533 template <typename Executor, typename R, typename T>
0534 class spawn_handler<Executor, R(T)>
0535 : public spawn_handler_base<Executor>
0536 {
0537 public:
0538 typedef T return_type;
0539 typedef return_type* result_type;
0540
0541 spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
0542 : spawn_handler_base<Executor>(yield),
0543 result_(result)
0544 {
0545 }
0546
0547 void operator()(T value)
0548 {
0549 result_ = &value;
0550 this->resume();
0551 }
0552
0553 static return_type on_resume(result_type& result)
0554 {
0555 return static_cast<return_type&&>(*result);
0556 }
0557
0558 private:
0559 result_type& result_;
0560 };
0561
0562 template <typename Executor, typename R, typename T>
0563 class spawn_handler<Executor, R(boost::system::error_code, T)>
0564 : public spawn_handler_base<Executor>
0565 {
0566 public:
0567 typedef T return_type;
0568
0569 struct result_type
0570 {
0571 boost::system::error_code* ec_;
0572 return_type* value_;
0573 };
0574
0575 spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
0576 : spawn_handler_base<Executor>(yield),
0577 result_(result)
0578 {
0579 }
0580
0581 void operator()(boost::system::error_code ec, T value)
0582 {
0583 if (this->yield_.ec_)
0584 {
0585 *this->yield_.ec_ = ec;
0586 result_.ec_ = 0;
0587 }
0588 else
0589 result_.ec_ = &ec;
0590 result_.value_ = &value;
0591 this->resume();
0592 }
0593
0594 static return_type on_resume(result_type& result)
0595 {
0596 if (result.ec_)
0597 throw_error(*result.ec_);
0598 return static_cast<return_type&&>(*result.value_);
0599 }
0600
0601 private:
0602 result_type& result_;
0603 };
0604
0605 template <typename Executor, typename R, typename T>
0606 class spawn_handler<Executor, R(exception_ptr, T)>
0607 : public spawn_handler_base<Executor>
0608 {
0609 public:
0610 typedef T return_type;
0611
0612 struct result_type
0613 {
0614 exception_ptr* ex_;
0615 return_type* value_;
0616 };
0617
0618 spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
0619 : spawn_handler_base<Executor>(yield),
0620 result_(result)
0621 {
0622 }
0623
0624 void operator()(exception_ptr ex, T value)
0625 {
0626 result_.ex_ = &ex;
0627 result_.value_ = &value;
0628 this->resume();
0629 }
0630
0631 static return_type on_resume(result_type& result)
0632 {
0633 if (*result.ex_)
0634 rethrow_exception(*result.ex_);
0635 return static_cast<return_type&&>(*result.value_);
0636 }
0637
0638 private:
0639 result_type& result_;
0640 };
0641
0642 template <typename Executor, typename R, typename... Ts>
0643 class spawn_handler<Executor, R(Ts...)>
0644 : public spawn_handler_base<Executor>
0645 {
0646 public:
0647 typedef std::tuple<Ts...> return_type;
0648
0649 typedef return_type* result_type;
0650
0651 spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
0652 : spawn_handler_base<Executor>(yield),
0653 result_(result)
0654 {
0655 }
0656
0657 template <typename... Args>
0658 void operator()(Args&&... args)
0659 {
0660 return_type value(static_cast<Args&&>(args)...);
0661 result_ = &value;
0662 this->resume();
0663 }
0664
0665 static return_type on_resume(result_type& result)
0666 {
0667 return static_cast<return_type&&>(*result);
0668 }
0669
0670 private:
0671 result_type& result_;
0672 };
0673
0674 template <typename Executor, typename R, typename... Ts>
0675 class spawn_handler<Executor, R(boost::system::error_code, Ts...)>
0676 : public spawn_handler_base<Executor>
0677 {
0678 public:
0679 typedef std::tuple<Ts...> return_type;
0680
0681 struct result_type
0682 {
0683 boost::system::error_code* ec_;
0684 return_type* value_;
0685 };
0686
0687 spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
0688 : spawn_handler_base<Executor>(yield),
0689 result_(result)
0690 {
0691 }
0692
0693 template <typename... Args>
0694 void operator()(boost::system::error_code ec,
0695 Args&&... args)
0696 {
0697 return_type value(static_cast<Args&&>(args)...);
0698 if (this->yield_.ec_)
0699 {
0700 *this->yield_.ec_ = ec;
0701 result_.ec_ = 0;
0702 }
0703 else
0704 result_.ec_ = &ec;
0705 result_.value_ = &value;
0706 this->resume();
0707 }
0708
0709 static return_type on_resume(result_type& result)
0710 {
0711 if (result.ec_)
0712 throw_error(*result.ec_);
0713 return static_cast<return_type&&>(*result.value_);
0714 }
0715
0716 private:
0717 result_type& result_;
0718 };
0719
0720 template <typename Executor, typename R, typename... Ts>
0721 class spawn_handler<Executor, R(exception_ptr, Ts...)>
0722 : public spawn_handler_base<Executor>
0723 {
0724 public:
0725 typedef std::tuple<Ts...> return_type;
0726
0727 struct result_type
0728 {
0729 exception_ptr* ex_;
0730 return_type* value_;
0731 };
0732
0733 spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
0734 : spawn_handler_base<Executor>(yield),
0735 result_(result)
0736 {
0737 }
0738
0739 template <typename... Args>
0740 void operator()(exception_ptr ex, Args&&... args)
0741 {
0742 return_type value(static_cast<Args&&>(args)...);
0743 result_.ex_ = &ex;
0744 result_.value_ = &value;
0745 this->resume();
0746 }
0747
0748 static return_type on_resume(result_type& result)
0749 {
0750 if (*result.ex_)
0751 rethrow_exception(*result.ex_);
0752 return static_cast<return_type&&>(*result.value_);
0753 }
0754
0755 private:
0756 result_type& result_;
0757 };
0758
0759 template <typename Executor, typename Signature>
0760 inline bool asio_handler_is_continuation(spawn_handler<Executor, Signature>*)
0761 {
0762 return true;
0763 }
0764
0765 }
0766
0767 template <typename Executor, typename Signature>
0768 class async_result<basic_yield_context<Executor>, Signature>
0769 {
0770 public:
0771 typedef typename detail::spawn_handler<Executor, Signature> handler_type;
0772 typedef typename handler_type::return_type return_type;
0773
0774 #if defined(BOOST_ASIO_HAS_VARIADIC_LAMBDA_CAPTURES)
0775
0776 template <typename Initiation, typename... InitArgs>
0777 static return_type initiate(Initiation&& init,
0778 const basic_yield_context<Executor>& yield,
0779 InitArgs&&... init_args)
0780 {
0781 typename handler_type::result_type result
0782 = typename handler_type::result_type();
0783
0784 yield.spawned_thread_->suspend_with(
0785 [&]()
0786 {
0787 static_cast<Initiation&&>(init)(
0788 handler_type(yield, result),
0789 static_cast<InitArgs&&>(init_args)...);
0790 });
0791
0792 return handler_type::on_resume(result);
0793 }
0794
0795 #else
0796
0797 template <typename Initiation, typename... InitArgs>
0798 struct suspend_with_helper
0799 {
0800 typename handler_type::result_type& result_;
0801 Initiation&& init_;
0802 const basic_yield_context<Executor>& yield_;
0803 std::tuple<InitArgs&&...> init_args_;
0804
0805 template <std::size_t... I>
0806 void do_invoke(detail::index_sequence<I...>)
0807 {
0808 static_cast<Initiation&&>(init_)(
0809 handler_type(yield_, result_),
0810 static_cast<InitArgs&&>(std::get<I>(init_args_))...);
0811 }
0812
0813 void operator()()
0814 {
0815 this->do_invoke(detail::make_index_sequence<sizeof...(InitArgs)>());
0816 }
0817 };
0818
0819 template <typename Initiation, typename... InitArgs>
0820 static return_type initiate(Initiation&& init,
0821 const basic_yield_context<Executor>& yield,
0822 InitArgs&&... init_args)
0823 {
0824 typename handler_type::result_type result
0825 = typename handler_type::result_type();
0826
0827 yield.spawned_thread_->suspend_with(
0828 suspend_with_helper<Initiation, InitArgs...>{
0829 result, static_cast<Initiation&&>(init), yield,
0830 std::tuple<InitArgs&&...>(
0831 static_cast<InitArgs&&>(init_args)...)});
0832
0833 return handler_type::on_resume(result);
0834 }
0835
0836 #endif
0837 };
0838
0839 namespace detail {
0840
0841 template <typename Executor, typename Function, typename Handler>
0842 class spawn_entry_point
0843 {
0844 public:
0845 template <typename F, typename H>
0846 spawn_entry_point(const Executor& ex,
0847 F&& f, H&& h)
0848 : executor_(ex),
0849 function_(static_cast<F&&>(f)),
0850 handler_(static_cast<H&&>(h)),
0851 work_(handler_, executor_)
0852 {
0853 }
0854
0855 void operator()(spawned_thread_base* spawned_thread)
0856 {
0857 const basic_yield_context<Executor> yield(spawned_thread, executor_);
0858 this->call(yield,
0859 void_type<result_of_t<Function(basic_yield_context<Executor>)>>());
0860 }
0861
0862 private:
0863 void call(const basic_yield_context<Executor>& yield, void_type<void>)
0864 {
0865 #if !defined(BOOST_ASIO_NO_EXCEPTIONS)
0866 try
0867 #endif
0868 {
0869 function_(yield);
0870 if (!yield.spawned_thread_->has_context_switched())
0871 (post)(yield);
0872 detail::binder1<Handler, exception_ptr>
0873 handler(handler_, exception_ptr());
0874 work_.complete(handler, handler.handler_);
0875 }
0876 #if !defined(BOOST_ASIO_NO_EXCEPTIONS)
0877 # if defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
0878 catch (const boost::context::detail::forced_unwind&)
0879 {
0880 throw;
0881 }
0882 # endif
0883 # if defined(BOOST_ASIO_HAS_BOOST_COROUTINE)
0884 catch (const boost::coroutines::detail::forced_unwind&)
0885 {
0886 throw;
0887 }
0888 # endif
0889 catch (...)
0890 {
0891 exception_ptr ex = current_exception();
0892 if (!yield.spawned_thread_->has_context_switched())
0893 (post)(yield);
0894 detail::binder1<Handler, exception_ptr> handler(handler_, ex);
0895 work_.complete(handler, handler.handler_);
0896 }
0897 #endif
0898 }
0899
0900 template <typename T>
0901 void call(const basic_yield_context<Executor>& yield, void_type<T>)
0902 {
0903 #if !defined(BOOST_ASIO_NO_EXCEPTIONS)
0904 try
0905 #endif
0906 {
0907 T result(function_(yield));
0908 if (!yield.spawned_thread_->has_context_switched())
0909 (post)(yield);
0910 detail::binder2<Handler, exception_ptr, T>
0911 handler(handler_, exception_ptr(), static_cast<T&&>(result));
0912 work_.complete(handler, handler.handler_);
0913 }
0914 #if !defined(BOOST_ASIO_NO_EXCEPTIONS)
0915 # if defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
0916 catch (const boost::context::detail::forced_unwind&)
0917 {
0918 throw;
0919 }
0920 # endif
0921 # if defined(BOOST_ASIO_HAS_BOOST_COROUTINE)
0922 catch (const boost::coroutines::detail::forced_unwind&)
0923 {
0924 throw;
0925 }
0926 # endif
0927 catch (...)
0928 {
0929 exception_ptr ex = current_exception();
0930 if (!yield.spawned_thread_->has_context_switched())
0931 (post)(yield);
0932 detail::binder2<Handler, exception_ptr, T> handler(handler_, ex, T());
0933 work_.complete(handler, handler.handler_);
0934 }
0935 #endif
0936 }
0937
0938 Executor executor_;
0939 Function function_;
0940 Handler handler_;
0941 handler_work<Handler, Executor> work_;
0942 };
0943
0944 struct spawn_cancellation_signal_emitter
0945 {
0946 cancellation_signal* signal_;
0947 cancellation_type_t type_;
0948
0949 void operator()()
0950 {
0951 signal_->emit(type_);
0952 }
0953 };
0954
0955 template <typename Handler, typename Executor, typename = void>
0956 class spawn_cancellation_handler
0957 {
0958 public:
0959 spawn_cancellation_handler(const Handler&, const Executor& ex)
0960 : ex_(ex)
0961 {
0962 }
0963
0964 cancellation_slot slot()
0965 {
0966 return signal_.slot();
0967 }
0968
0969 void operator()(cancellation_type_t type)
0970 {
0971 spawn_cancellation_signal_emitter emitter = { &signal_, type };
0972 (dispatch)(ex_, emitter);
0973 }
0974
0975 private:
0976 cancellation_signal signal_;
0977 Executor ex_;
0978 };
0979
0980 template <typename Handler, typename Executor>
0981 class spawn_cancellation_handler<Handler, Executor,
0982 enable_if_t<
0983 is_same<
0984 typename associated_executor<Handler,
0985 Executor>::asio_associated_executor_is_unspecialised,
0986 void
0987 >::value
0988 >>
0989 {
0990 public:
0991 spawn_cancellation_handler(const Handler&, const Executor&)
0992 {
0993 }
0994
0995 cancellation_slot slot()
0996 {
0997 return signal_.slot();
0998 }
0999
1000 void operator()(cancellation_type_t type)
1001 {
1002 signal_.emit(type);
1003 }
1004
1005 private:
1006 cancellation_signal signal_;
1007 };
1008
1009 template <typename Executor>
1010 class initiate_spawn
1011 {
1012 public:
1013 typedef Executor executor_type;
1014
1015 explicit initiate_spawn(const executor_type& ex)
1016 : executor_(ex)
1017 {
1018 }
1019
1020 executor_type get_executor() const noexcept
1021 {
1022 return executor_;
1023 }
1024
1025 template <typename Handler, typename F>
1026 void operator()(Handler&& handler,
1027 F&& f) const
1028 {
1029 typedef decay_t<Handler> handler_type;
1030 typedef decay_t<F> function_type;
1031 typedef spawn_cancellation_handler<
1032 handler_type, Executor> cancel_handler_type;
1033
1034 associated_cancellation_slot_t<handler_type> slot
1035 = boost::asio::get_associated_cancellation_slot(handler);
1036
1037 cancel_handler_type* cancel_handler = slot.is_connected()
1038 ? &slot.template emplace<cancel_handler_type>(handler, executor_)
1039 : 0;
1040
1041 cancellation_slot proxy_slot(
1042 cancel_handler
1043 ? cancel_handler->slot()
1044 : cancellation_slot());
1045
1046 cancellation_state cancel_state(proxy_slot);
1047
1048 (dispatch)(executor_,
1049 spawned_thread_resumer(
1050 default_spawned_thread_type::spawn(
1051 spawn_entry_point<Executor, function_type, handler_type>(
1052 executor_, static_cast<F&&>(f),
1053 static_cast<Handler&&>(handler)),
1054 proxy_slot, cancel_state)));
1055 }
1056
1057 #if defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
1058
1059 template <typename Handler, typename StackAllocator, typename F>
1060 void operator()(Handler&& handler, allocator_arg_t,
1061 StackAllocator&& stack_allocator,
1062 F&& f) const
1063 {
1064 typedef decay_t<Handler> handler_type;
1065 typedef decay_t<F> function_type;
1066 typedef spawn_cancellation_handler<
1067 handler_type, Executor> cancel_handler_type;
1068
1069 associated_cancellation_slot_t<handler_type> slot
1070 = boost::asio::get_associated_cancellation_slot(handler);
1071
1072 cancel_handler_type* cancel_handler = slot.is_connected()
1073 ? &slot.template emplace<cancel_handler_type>(handler, executor_)
1074 : 0;
1075
1076 cancellation_slot proxy_slot(
1077 cancel_handler
1078 ? cancel_handler->slot()
1079 : cancellation_slot());
1080
1081 cancellation_state cancel_state(proxy_slot);
1082
1083 (dispatch)(executor_,
1084 spawned_thread_resumer(
1085 spawned_fiber_thread::spawn(allocator_arg_t(),
1086 static_cast<StackAllocator&&>(stack_allocator),
1087 spawn_entry_point<Executor, function_type, handler_type>(
1088 executor_, static_cast<F&&>(f),
1089 static_cast<Handler&&>(handler)),
1090 proxy_slot, cancel_state)));
1091 }
1092
1093 #endif
1094
1095 private:
1096 executor_type executor_;
1097 };
1098
1099 }
1100
1101 template <typename Executor, typename F,
1102 BOOST_ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature<
1103 result_of_t<F(basic_yield_context<Executor>)>>::type) CompletionToken>
1104 inline auto spawn(const Executor& ex, F&& function, CompletionToken&& token,
1105 #if defined(BOOST_ASIO_HAS_BOOST_COROUTINE)
1106 constraint_t<
1107 !is_same<
1108 decay_t<CompletionToken>,
1109 boost::coroutines::attributes
1110 >::value
1111 >,
1112 #endif
1113 constraint_t<
1114 is_executor<Executor>::value || execution::is_executor<Executor>::value
1115 >)
1116 -> decltype(
1117 async_initiate<CompletionToken,
1118 typename detail::spawn_signature<
1119 result_of_t<F(basic_yield_context<Executor>)>>::type>(
1120 declval<detail::initiate_spawn<Executor>>(),
1121 token, static_cast<F&&>(function)))
1122 {
1123 return async_initiate<CompletionToken,
1124 typename detail::spawn_signature<
1125 result_of_t<F(basic_yield_context<Executor>)>>::type>(
1126 detail::initiate_spawn<Executor>(ex),
1127 token, static_cast<F&&>(function));
1128 }
1129
1130 template <typename ExecutionContext, typename F,
1131 BOOST_ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature<
1132 result_of_t<F(basic_yield_context<
1133 typename ExecutionContext::executor_type>)>>::type) CompletionToken>
1134 inline auto spawn(ExecutionContext& ctx, F&& function, CompletionToken&& token,
1135 #if defined(BOOST_ASIO_HAS_BOOST_COROUTINE)
1136 constraint_t<
1137 !is_same<
1138 decay_t<CompletionToken>,
1139 boost::coroutines::attributes
1140 >::value
1141 >,
1142 #endif
1143 constraint_t<
1144 is_convertible<ExecutionContext&, execution_context&>::value
1145 >)
1146 -> decltype(
1147 async_initiate<CompletionToken,
1148 typename detail::spawn_signature<
1149 result_of_t<F(basic_yield_context<
1150 typename ExecutionContext::executor_type>)>>::type>(
1151 declval<detail::initiate_spawn<
1152 typename ExecutionContext::executor_type>>(),
1153 token, static_cast<F&&>(function)))
1154 {
1155 return (spawn)(ctx.get_executor(), static_cast<F&&>(function),
1156 static_cast<CompletionToken&&>(token));
1157 }
1158
1159 template <typename Executor, typename F,
1160 BOOST_ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature<
1161 result_of_t<F(basic_yield_context<Executor>)>>::type)
1162 CompletionToken>
1163 inline auto spawn(const basic_yield_context<Executor>& ctx,
1164 F&& function, CompletionToken&& token,
1165 #if defined(BOOST_ASIO_HAS_BOOST_COROUTINE)
1166 constraint_t<
1167 !is_same<
1168 decay_t<CompletionToken>,
1169 boost::coroutines::attributes
1170 >::value
1171 >,
1172 #endif
1173 constraint_t<
1174 is_executor<Executor>::value || execution::is_executor<Executor>::value
1175 >)
1176 -> decltype(
1177 async_initiate<CompletionToken,
1178 typename detail::spawn_signature<
1179 result_of_t<F(basic_yield_context<Executor>)>>::type>(
1180 declval<detail::initiate_spawn<Executor>>(),
1181 token, static_cast<F&&>(function)))
1182 {
1183 return (spawn)(ctx.get_executor(), static_cast<F&&>(function),
1184 static_cast<CompletionToken&&>(token));
1185 }
1186
1187 #if defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
1188
1189 template <typename Executor, typename StackAllocator, typename F,
1190 BOOST_ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature<
1191 result_of_t<F(basic_yield_context<Executor>)>>::type)
1192 CompletionToken>
1193 inline auto spawn(const Executor& ex, allocator_arg_t,
1194 StackAllocator&& stack_allocator, F&& function, CompletionToken&& token,
1195 constraint_t<
1196 is_executor<Executor>::value || execution::is_executor<Executor>::value
1197 >)
1198 -> decltype(
1199 async_initiate<CompletionToken,
1200 typename detail::spawn_signature<
1201 result_of_t<F(basic_yield_context<Executor>)>>::type>(
1202 declval<detail::initiate_spawn<Executor>>(),
1203 token, allocator_arg_t(),
1204 static_cast<StackAllocator&&>(stack_allocator),
1205 static_cast<F&&>(function)))
1206 {
1207 return async_initiate<CompletionToken,
1208 typename detail::spawn_signature<
1209 result_of_t<F(basic_yield_context<Executor>)>>::type>(
1210 detail::initiate_spawn<Executor>(ex), token, allocator_arg_t(),
1211 static_cast<StackAllocator&&>(stack_allocator),
1212 static_cast<F&&>(function));
1213 }
1214
1215 template <typename ExecutionContext, typename StackAllocator, typename F,
1216 BOOST_ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature<
1217 result_of_t<F(basic_yield_context<
1218 typename ExecutionContext::executor_type>)>>::type) CompletionToken>
1219 inline auto spawn(ExecutionContext& ctx, allocator_arg_t,
1220 StackAllocator&& stack_allocator, F&& function, CompletionToken&& token,
1221 constraint_t<
1222 is_convertible<ExecutionContext&, execution_context&>::value
1223 >)
1224 -> decltype(
1225 async_initiate<CompletionToken,
1226 typename detail::spawn_signature<
1227 result_of_t<F(basic_yield_context<
1228 typename ExecutionContext::executor_type>)>>::type>(
1229 declval<detail::initiate_spawn<
1230 typename ExecutionContext::executor_type>>(),
1231 token, allocator_arg_t(),
1232 static_cast<StackAllocator&&>(stack_allocator),
1233 static_cast<F&&>(function)))
1234 {
1235 return (spawn)(ctx.get_executor(), allocator_arg_t(),
1236 static_cast<StackAllocator&&>(stack_allocator),
1237 static_cast<F&&>(function), static_cast<CompletionToken&&>(token));
1238 }
1239
1240 template <typename Executor, typename StackAllocator, typename F,
1241 BOOST_ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature<
1242 result_of_t<F(basic_yield_context<Executor>)>>::type) CompletionToken>
1243 inline auto spawn(const basic_yield_context<Executor>& ctx, allocator_arg_t,
1244 StackAllocator&& stack_allocator, F&& function, CompletionToken&& token,
1245 constraint_t<
1246 is_executor<Executor>::value || execution::is_executor<Executor>::value
1247 >)
1248 -> decltype(
1249 async_initiate<CompletionToken,
1250 typename detail::spawn_signature<
1251 result_of_t<F(basic_yield_context<Executor>)>>::type>(
1252 declval<detail::initiate_spawn<Executor>>(), token,
1253 allocator_arg_t(), static_cast<StackAllocator&&>(stack_allocator),
1254 static_cast<F&&>(function)))
1255 {
1256 return (spawn)(ctx.get_executor(), allocator_arg_t(),
1257 static_cast<StackAllocator&&>(stack_allocator),
1258 static_cast<F&&>(function), static_cast<CompletionToken&&>(token));
1259 }
1260
1261 #endif
1262
1263 #if defined(BOOST_ASIO_HAS_BOOST_COROUTINE)
1264
1265 namespace detail {
1266
1267 template <typename Executor, typename Function, typename Handler>
1268 class old_spawn_entry_point
1269 {
1270 public:
1271 template <typename F, typename H>
1272 old_spawn_entry_point(const Executor& ex, F&& f, H&& h)
1273 : executor_(ex),
1274 function_(static_cast<F&&>(f)),
1275 handler_(static_cast<H&&>(h))
1276 {
1277 }
1278
1279 void operator()(spawned_thread_base* spawned_thread)
1280 {
1281 const basic_yield_context<Executor> yield(spawned_thread, executor_);
1282 this->call(yield,
1283 void_type<result_of_t<Function(basic_yield_context<Executor>)>>());
1284 }
1285
1286 private:
1287 void call(const basic_yield_context<Executor>& yield, void_type<void>)
1288 {
1289 function_(yield);
1290 static_cast<Handler&&>(handler_)();
1291 }
1292
1293 template <typename T>
1294 void call(const basic_yield_context<Executor>& yield, void_type<T>)
1295 {
1296 static_cast<Handler&&>(handler_)(function_(yield));
1297 }
1298
1299 Executor executor_;
1300 Function function_;
1301 Handler handler_;
1302 };
1303
1304 inline void default_spawn_handler() {}
1305
1306 }
1307
1308 template <typename Function>
1309 inline void spawn(Function&& function,
1310 const boost::coroutines::attributes& attributes)
1311 {
1312 associated_executor_t<decay_t<Function>> ex(
1313 (get_associated_executor)(function));
1314
1315 boost::asio::spawn(ex, static_cast<Function&&>(function), attributes);
1316 }
1317
1318 template <typename Handler, typename Function>
1319 void spawn(Handler&& handler, Function&& function,
1320 const boost::coroutines::attributes& attributes,
1321 constraint_t<
1322 !is_executor<decay_t<Handler>>::value &&
1323 !execution::is_executor<decay_t<Handler>>::value &&
1324 !is_convertible<Handler&, execution_context&>::value>)
1325 {
1326 typedef associated_executor_t<decay_t<Handler>> executor_type;
1327 executor_type ex((get_associated_executor)(handler));
1328
1329 (dispatch)(ex,
1330 detail::spawned_thread_resumer(
1331 detail::spawned_coroutine_thread::spawn(
1332 detail::old_spawn_entry_point<executor_type,
1333 decay_t<Function>, void (*)()>(
1334 ex, static_cast<Function&&>(function),
1335 &detail::default_spawn_handler), attributes)));
1336 }
1337
1338 template <typename Executor, typename Function>
1339 void spawn(basic_yield_context<Executor> ctx, Function&& function,
1340 const boost::coroutines::attributes& attributes)
1341 {
1342 (dispatch)(ctx.get_executor(),
1343 detail::spawned_thread_resumer(
1344 detail::spawned_coroutine_thread::spawn(
1345 detail::old_spawn_entry_point<Executor,
1346 decay_t<Function>, void (*)()>(
1347 ctx.get_executor(), static_cast<Function&&>(function),
1348 &detail::default_spawn_handler), attributes)));
1349 }
1350
1351 template <typename Function, typename Executor>
1352 inline void spawn(const Executor& ex, Function&& function,
1353 const boost::coroutines::attributes& attributes,
1354 constraint_t<
1355 is_executor<Executor>::value || execution::is_executor<Executor>::value
1356 >)
1357 {
1358 boost::asio::spawn(boost::asio::strand<Executor>(ex),
1359 static_cast<Function&&>(function), attributes);
1360 }
1361
1362 template <typename Function, typename Executor>
1363 inline void spawn(const strand<Executor>& ex, Function&& function,
1364 const boost::coroutines::attributes& attributes)
1365 {
1366 boost::asio::spawn(boost::asio::bind_executor(
1367 ex, &detail::default_spawn_handler),
1368 static_cast<Function&&>(function), attributes);
1369 }
1370
1371 #if !defined(BOOST_ASIO_NO_TS_EXECUTORS)
1372
1373 template <typename Function>
1374 inline void spawn(const boost::asio::io_context::strand& s, Function&& function,
1375 const boost::coroutines::attributes& attributes)
1376 {
1377 boost::asio::spawn(boost::asio::bind_executor(
1378 s, &detail::default_spawn_handler),
1379 static_cast<Function&&>(function), attributes);
1380 }
1381
1382 #endif
1383
1384 template <typename Function, typename ExecutionContext>
1385 inline void spawn(ExecutionContext& ctx, Function&& function,
1386 const boost::coroutines::attributes& attributes,
1387 constraint_t<
1388 is_convertible<ExecutionContext&, execution_context&>::value
1389 >)
1390 {
1391 boost::asio::spawn(ctx.get_executor(),
1392 static_cast<Function&&>(function), attributes);
1393 }
1394
1395 #endif
1396
1397 }
1398 }
1399
1400 #include <boost/asio/detail/pop_options.hpp>
1401
1402 #endif