File indexing completed on 2025-01-18 09:28:57
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/system/system_error.hpp>
0033
0034 #if defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
0035 # include <boost/context/fiber.hpp>
0036 #endif
0037
0038 #include <boost/asio/detail/push_options.hpp>
0039
0040 namespace boost {
0041 namespace asio {
0042 namespace detail {
0043
0044 #if !defined(BOOST_ASIO_NO_EXCEPTIONS)
0045 inline void spawned_thread_rethrow(void* ex)
0046 {
0047 if (*static_cast<exception_ptr*>(ex))
0048 rethrow_exception(*static_cast<exception_ptr*>(ex));
0049 }
0050 #endif
0051
0052 #if defined(BOOST_ASIO_HAS_BOOST_COROUTINE)
0053
0054
0055 class spawned_coroutine_thread : public spawned_thread_base
0056 {
0057 public:
0058 #if defined(BOOST_COROUTINES_UNIDIRECT) || defined(BOOST_COROUTINES_V2)
0059 typedef boost::coroutines::pull_coroutine<void> callee_type;
0060 typedef boost::coroutines::push_coroutine<void> caller_type;
0061 #else
0062 typedef boost::coroutines::coroutine<void()> callee_type;
0063 typedef boost::coroutines::coroutine<void()> caller_type;
0064 #endif
0065
0066 spawned_coroutine_thread(caller_type& caller)
0067 : caller_(caller),
0068 on_suspend_fn_(0),
0069 on_suspend_arg_(0)
0070 {
0071 }
0072
0073 template <typename F>
0074 static spawned_thread_base* spawn(F&& f,
0075 const boost::coroutines::attributes& attributes,
0076 cancellation_slot parent_cancel_slot = cancellation_slot(),
0077 cancellation_state cancel_state = cancellation_state())
0078 {
0079 spawned_coroutine_thread* spawned_thread = 0;
0080 callee_type callee(entry_point<decay_t<F>>(
0081 static_cast<F&&>(f), &spawned_thread), attributes);
0082 spawned_thread->callee_.swap(callee);
0083 spawned_thread->parent_cancellation_slot_ = parent_cancel_slot;
0084 spawned_thread->cancellation_state_ = cancel_state;
0085 return spawned_thread;
0086 }
0087
0088 template <typename F>
0089 static spawned_thread_base* spawn(F&& f,
0090 cancellation_slot parent_cancel_slot = cancellation_slot(),
0091 cancellation_state cancel_state = cancellation_state())
0092 {
0093 return spawn(static_cast<F&&>(f), boost::coroutines::attributes(),
0094 parent_cancel_slot, cancel_state);
0095 }
0096
0097 void resume()
0098 {
0099 callee_();
0100 if (on_suspend_fn_)
0101 {
0102 void (*fn)(void*) = on_suspend_fn_;
0103 void* arg = on_suspend_arg_;
0104 on_suspend_fn_ = 0;
0105 fn(arg);
0106 }
0107 }
0108
0109 void suspend_with(void (*fn)(void*), void* arg)
0110 {
0111 if (throw_if_cancelled_)
0112 if (!!cancellation_state_.cancelled())
0113 throw_error(boost::asio::error::operation_aborted, "yield");
0114 has_context_switched_ = true;
0115 on_suspend_fn_ = fn;
0116 on_suspend_arg_ = arg;
0117 caller_();
0118 }
0119
0120 void destroy()
0121 {
0122 callee_type callee;
0123 callee.swap(callee_);
0124 if (terminal_)
0125 callee();
0126 }
0127
0128 private:
0129 template <typename Function>
0130 class entry_point
0131 {
0132 public:
0133 template <typename F>
0134 entry_point(F&& f,
0135 spawned_coroutine_thread** spawned_thread_out)
0136 : function_(static_cast<F&&>(f)),
0137 spawned_thread_out_(spawned_thread_out)
0138 {
0139 }
0140
0141 void operator()(caller_type& caller)
0142 {
0143 Function function(static_cast<Function&&>(function_));
0144 spawned_coroutine_thread spawned_thread(caller);
0145 *spawned_thread_out_ = &spawned_thread;
0146 spawned_thread_out_ = 0;
0147 spawned_thread.suspend();
0148 #if !defined(BOOST_ASIO_NO_EXCEPTIONS)
0149 try
0150 #endif
0151 {
0152 function(&spawned_thread);
0153 spawned_thread.terminal_ = true;
0154 spawned_thread.suspend();
0155 }
0156 #if !defined(BOOST_ASIO_NO_EXCEPTIONS)
0157 catch (const boost::coroutines::detail::forced_unwind&)
0158 {
0159 throw;
0160 }
0161 catch (...)
0162 {
0163 exception_ptr ex = current_exception();
0164 spawned_thread.terminal_ = true;
0165 spawned_thread.suspend_with(spawned_thread_rethrow, &ex);
0166 }
0167 #endif
0168 }
0169
0170 private:
0171 Function function_;
0172 spawned_coroutine_thread** spawned_thread_out_;
0173 };
0174
0175 caller_type& caller_;
0176 callee_type callee_;
0177 void (*on_suspend_fn_)(void*);
0178 void* on_suspend_arg_;
0179 };
0180
0181 #endif
0182
0183 #if defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
0184
0185
0186 class spawned_fiber_thread : public spawned_thread_base
0187 {
0188 public:
0189 typedef boost::context::fiber fiber_type;
0190
0191 spawned_fiber_thread(fiber_type&& caller)
0192 : caller_(static_cast<fiber_type&&>(caller)),
0193 on_suspend_fn_(0),
0194 on_suspend_arg_(0)
0195 {
0196 }
0197
0198 template <typename StackAllocator, typename F>
0199 static spawned_thread_base* spawn(allocator_arg_t,
0200 StackAllocator&& stack_allocator,
0201 F&& f,
0202 cancellation_slot parent_cancel_slot = cancellation_slot(),
0203 cancellation_state cancel_state = cancellation_state())
0204 {
0205 spawned_fiber_thread* spawned_thread = 0;
0206 fiber_type callee(allocator_arg_t(),
0207 static_cast<StackAllocator&&>(stack_allocator),
0208 entry_point<decay_t<F>>(
0209 static_cast<F&&>(f), &spawned_thread));
0210 callee = fiber_type(static_cast<fiber_type&&>(callee)).resume();
0211 spawned_thread->callee_ = static_cast<fiber_type&&>(callee);
0212 spawned_thread->parent_cancellation_slot_ = parent_cancel_slot;
0213 spawned_thread->cancellation_state_ = cancel_state;
0214 return spawned_thread;
0215 }
0216
0217 template <typename F>
0218 static spawned_thread_base* spawn(F&& f,
0219 cancellation_slot parent_cancel_slot = cancellation_slot(),
0220 cancellation_state cancel_state = cancellation_state())
0221 {
0222 return spawn(allocator_arg_t(), boost::context::fixedsize_stack(),
0223 static_cast<F&&>(f), parent_cancel_slot, cancel_state);
0224 }
0225
0226 void resume()
0227 {
0228 callee_ = fiber_type(static_cast<fiber_type&&>(callee_)).resume();
0229 if (on_suspend_fn_)
0230 {
0231 void (*fn)(void*) = on_suspend_fn_;
0232 void* arg = on_suspend_arg_;
0233 on_suspend_fn_ = 0;
0234 fn(arg);
0235 }
0236 }
0237
0238 void suspend_with(void (*fn)(void*), void* arg)
0239 {
0240 if (throw_if_cancelled_)
0241 if (!!cancellation_state_.cancelled())
0242 throw_error(boost::asio::error::operation_aborted, "yield");
0243 has_context_switched_ = true;
0244 on_suspend_fn_ = fn;
0245 on_suspend_arg_ = arg;
0246 caller_ = fiber_type(static_cast<fiber_type&&>(caller_)).resume();
0247 }
0248
0249 void destroy()
0250 {
0251 fiber_type callee = static_cast<fiber_type&&>(callee_);
0252 if (terminal_)
0253 fiber_type(static_cast<fiber_type&&>(callee)).resume();
0254 }
0255
0256 private:
0257 template <typename Function>
0258 class entry_point
0259 {
0260 public:
0261 template <typename F>
0262 entry_point(F&& f,
0263 spawned_fiber_thread** spawned_thread_out)
0264 : function_(static_cast<F&&>(f)),
0265 spawned_thread_out_(spawned_thread_out)
0266 {
0267 }
0268
0269 fiber_type operator()(fiber_type&& caller)
0270 {
0271 Function function(static_cast<Function&&>(function_));
0272 spawned_fiber_thread spawned_thread(
0273 static_cast<fiber_type&&>(caller));
0274 *spawned_thread_out_ = &spawned_thread;
0275 spawned_thread_out_ = 0;
0276 spawned_thread.suspend();
0277 #if !defined(BOOST_ASIO_NO_EXCEPTIONS)
0278 try
0279 #endif
0280 {
0281 function(&spawned_thread);
0282 spawned_thread.terminal_ = true;
0283 spawned_thread.suspend();
0284 }
0285 #if !defined(BOOST_ASIO_NO_EXCEPTIONS)
0286 catch (const boost::context::detail::forced_unwind&)
0287 {
0288 throw;
0289 }
0290 catch (...)
0291 {
0292 exception_ptr ex = current_exception();
0293 spawned_thread.terminal_ = true;
0294 spawned_thread.suspend_with(spawned_thread_rethrow, &ex);
0295 }
0296 #endif
0297 return static_cast<fiber_type&&>(spawned_thread.caller_);
0298 }
0299
0300 private:
0301 Function function_;
0302 spawned_fiber_thread** spawned_thread_out_;
0303 };
0304
0305 fiber_type caller_;
0306 fiber_type callee_;
0307 void (*on_suspend_fn_)(void*);
0308 void* on_suspend_arg_;
0309 };
0310
0311 #endif
0312
0313 #if defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
0314 typedef spawned_fiber_thread default_spawned_thread_type;
0315 #elif defined(BOOST_ASIO_HAS_BOOST_COROUTINE)
0316 typedef spawned_coroutine_thread default_spawned_thread_type;
0317 #else
0318 # error No spawn() implementation available
0319 #endif
0320
0321
0322 class spawned_thread_resumer
0323 {
0324 public:
0325 explicit spawned_thread_resumer(spawned_thread_base* spawned_thread)
0326 : spawned_thread_(spawned_thread)
0327 {
0328 }
0329
0330 spawned_thread_resumer(spawned_thread_resumer&& other) noexcept
0331 : spawned_thread_(other.spawned_thread_)
0332 {
0333 other.spawned_thread_ = 0;
0334 }
0335
0336 ~spawned_thread_resumer()
0337 {
0338 if (spawned_thread_)
0339 spawned_thread_->destroy();
0340 }
0341
0342 void operator()()
0343 {
0344 spawned_thread_->attach(&spawned_thread_);
0345 spawned_thread_->resume();
0346 }
0347
0348 private:
0349 spawned_thread_base* spawned_thread_;
0350 };
0351
0352
0353 class spawned_thread_destroyer
0354 {
0355 public:
0356 explicit spawned_thread_destroyer(spawned_thread_base* spawned_thread)
0357 : spawned_thread_(spawned_thread)
0358 {
0359 spawned_thread->detach();
0360 }
0361
0362 spawned_thread_destroyer(spawned_thread_destroyer&& other) noexcept
0363 : spawned_thread_(other.spawned_thread_)
0364 {
0365 other.spawned_thread_ = 0;
0366 }
0367
0368 ~spawned_thread_destroyer()
0369 {
0370 if (spawned_thread_)
0371 spawned_thread_->destroy();
0372 }
0373
0374 void operator()()
0375 {
0376 if (spawned_thread_)
0377 {
0378 spawned_thread_->destroy();
0379 spawned_thread_ = 0;
0380 }
0381 }
0382
0383 private:
0384 spawned_thread_base* spawned_thread_;
0385 };
0386
0387
0388 template <typename Executor>
0389 class spawn_handler_base
0390 {
0391 public:
0392 typedef Executor executor_type;
0393 typedef cancellation_slot cancellation_slot_type;
0394
0395 spawn_handler_base(const basic_yield_context<Executor>& yield)
0396 : yield_(yield),
0397 spawned_thread_(yield.spawned_thread_)
0398 {
0399 spawned_thread_->detach();
0400 }
0401
0402 spawn_handler_base(spawn_handler_base&& other) noexcept
0403 : yield_(other.yield_),
0404 spawned_thread_(other.spawned_thread_)
0405
0406 {
0407 other.spawned_thread_ = 0;
0408 }
0409
0410 ~spawn_handler_base()
0411 {
0412 if (spawned_thread_)
0413 (post)(yield_.executor_, spawned_thread_destroyer(spawned_thread_));
0414 }
0415
0416 executor_type get_executor() const noexcept
0417 {
0418 return yield_.executor_;
0419 }
0420
0421 cancellation_slot_type get_cancellation_slot() const noexcept
0422 {
0423 return spawned_thread_->get_cancellation_slot();
0424 }
0425
0426 void resume()
0427 {
0428 spawned_thread_resumer resumer(spawned_thread_);
0429 spawned_thread_ = 0;
0430 resumer();
0431 }
0432
0433 protected:
0434 const basic_yield_context<Executor>& yield_;
0435 spawned_thread_base* spawned_thread_;
0436 };
0437
0438
0439 template <typename Executor, typename Signature>
0440 class spawn_handler;
0441
0442 template <typename Executor, typename R>
0443 class spawn_handler<Executor, R()>
0444 : public spawn_handler_base<Executor>
0445 {
0446 public:
0447 typedef void return_type;
0448
0449 struct result_type {};
0450
0451 spawn_handler(const basic_yield_context<Executor>& yield, result_type&)
0452 : spawn_handler_base<Executor>(yield)
0453 {
0454 }
0455
0456 void operator()()
0457 {
0458 this->resume();
0459 }
0460
0461 static return_type on_resume(result_type&)
0462 {
0463 }
0464 };
0465
0466 template <typename Executor, typename R>
0467 class spawn_handler<Executor, R(boost::system::error_code)>
0468 : public spawn_handler_base<Executor>
0469 {
0470 public:
0471 typedef void return_type;
0472 typedef boost::system::error_code* result_type;
0473
0474 spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
0475 : spawn_handler_base<Executor>(yield),
0476 result_(result)
0477 {
0478 }
0479
0480 void operator()(boost::system::error_code ec)
0481 {
0482 if (this->yield_.ec_)
0483 {
0484 *this->yield_.ec_ = ec;
0485 result_ = 0;
0486 }
0487 else
0488 result_ = &ec;
0489 this->resume();
0490 }
0491
0492 static return_type on_resume(result_type& result)
0493 {
0494 if (result)
0495 throw_error(*result);
0496 }
0497
0498 private:
0499 result_type& result_;
0500 };
0501
0502 template <typename Executor, typename R>
0503 class spawn_handler<Executor, R(exception_ptr)>
0504 : public spawn_handler_base<Executor>
0505 {
0506 public:
0507 typedef void return_type;
0508 typedef exception_ptr* result_type;
0509
0510 spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
0511 : spawn_handler_base<Executor>(yield),
0512 result_(result)
0513 {
0514 }
0515
0516 void operator()(exception_ptr ex)
0517 {
0518 result_ = &ex;
0519 this->resume();
0520 }
0521
0522 static return_type on_resume(result_type& result)
0523 {
0524 if (*result)
0525 rethrow_exception(*result);
0526 }
0527
0528 private:
0529 result_type& result_;
0530 };
0531
0532 template <typename Executor, typename R, typename T>
0533 class spawn_handler<Executor, R(T)>
0534 : public spawn_handler_base<Executor>
0535 {
0536 public:
0537 typedef T return_type;
0538 typedef return_type* result_type;
0539
0540 spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
0541 : spawn_handler_base<Executor>(yield),
0542 result_(result)
0543 {
0544 }
0545
0546 void operator()(T value)
0547 {
0548 result_ = &value;
0549 this->resume();
0550 }
0551
0552 static return_type on_resume(result_type& result)
0553 {
0554 return static_cast<return_type&&>(*result);
0555 }
0556
0557 private:
0558 result_type& result_;
0559 };
0560
0561 template <typename Executor, typename R, typename T>
0562 class spawn_handler<Executor, R(boost::system::error_code, T)>
0563 : public spawn_handler_base<Executor>
0564 {
0565 public:
0566 typedef T return_type;
0567
0568 struct result_type
0569 {
0570 boost::system::error_code* ec_;
0571 return_type* value_;
0572 };
0573
0574 spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
0575 : spawn_handler_base<Executor>(yield),
0576 result_(result)
0577 {
0578 }
0579
0580 void operator()(boost::system::error_code ec, T value)
0581 {
0582 if (this->yield_.ec_)
0583 {
0584 *this->yield_.ec_ = ec;
0585 result_.ec_ = 0;
0586 }
0587 else
0588 result_.ec_ = &ec;
0589 result_.value_ = &value;
0590 this->resume();
0591 }
0592
0593 static return_type on_resume(result_type& result)
0594 {
0595 if (result.ec_)
0596 throw_error(*result.ec_);
0597 return static_cast<return_type&&>(*result.value_);
0598 }
0599
0600 private:
0601 result_type& result_;
0602 };
0603
0604 template <typename Executor, typename R, typename T>
0605 class spawn_handler<Executor, R(exception_ptr, T)>
0606 : public spawn_handler_base<Executor>
0607 {
0608 public:
0609 typedef T return_type;
0610
0611 struct result_type
0612 {
0613 exception_ptr* ex_;
0614 return_type* value_;
0615 };
0616
0617 spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
0618 : spawn_handler_base<Executor>(yield),
0619 result_(result)
0620 {
0621 }
0622
0623 void operator()(exception_ptr ex, T value)
0624 {
0625 result_.ex_ = &ex;
0626 result_.value_ = &value;
0627 this->resume();
0628 }
0629
0630 static return_type on_resume(result_type& result)
0631 {
0632 if (*result.ex_)
0633 rethrow_exception(*result.ex_);
0634 return static_cast<return_type&&>(*result.value_);
0635 }
0636
0637 private:
0638 result_type& result_;
0639 };
0640
0641 template <typename Executor, typename R, typename... Ts>
0642 class spawn_handler<Executor, R(Ts...)>
0643 : public spawn_handler_base<Executor>
0644 {
0645 public:
0646 typedef std::tuple<Ts...> return_type;
0647
0648 typedef return_type* result_type;
0649
0650 spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
0651 : spawn_handler_base<Executor>(yield),
0652 result_(result)
0653 {
0654 }
0655
0656 template <typename... Args>
0657 void operator()(Args&&... args)
0658 {
0659 return_type value(static_cast<Args&&>(args)...);
0660 result_ = &value;
0661 this->resume();
0662 }
0663
0664 static return_type on_resume(result_type& result)
0665 {
0666 return static_cast<return_type&&>(*result);
0667 }
0668
0669 private:
0670 result_type& result_;
0671 };
0672
0673 template <typename Executor, typename R, typename... Ts>
0674 class spawn_handler<Executor, R(boost::system::error_code, Ts...)>
0675 : public spawn_handler_base<Executor>
0676 {
0677 public:
0678 typedef std::tuple<Ts...> return_type;
0679
0680 struct result_type
0681 {
0682 boost::system::error_code* ec_;
0683 return_type* value_;
0684 };
0685
0686 spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
0687 : spawn_handler_base<Executor>(yield),
0688 result_(result)
0689 {
0690 }
0691
0692 template <typename... Args>
0693 void operator()(boost::system::error_code ec,
0694 Args&&... args)
0695 {
0696 return_type value(static_cast<Args&&>(args)...);
0697 if (this->yield_.ec_)
0698 {
0699 *this->yield_.ec_ = ec;
0700 result_.ec_ = 0;
0701 }
0702 else
0703 result_.ec_ = &ec;
0704 result_.value_ = &value;
0705 this->resume();
0706 }
0707
0708 static return_type on_resume(result_type& result)
0709 {
0710 if (result.ec_)
0711 throw_error(*result.ec_);
0712 return static_cast<return_type&&>(*result.value_);
0713 }
0714
0715 private:
0716 result_type& result_;
0717 };
0718
0719 template <typename Executor, typename R, typename... Ts>
0720 class spawn_handler<Executor, R(exception_ptr, Ts...)>
0721 : public spawn_handler_base<Executor>
0722 {
0723 public:
0724 typedef std::tuple<Ts...> return_type;
0725
0726 struct result_type
0727 {
0728 exception_ptr* ex_;
0729 return_type* value_;
0730 };
0731
0732 spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
0733 : spawn_handler_base<Executor>(yield),
0734 result_(result)
0735 {
0736 }
0737
0738 template <typename... Args>
0739 void operator()(exception_ptr ex, Args&&... args)
0740 {
0741 return_type value(static_cast<Args&&>(args)...);
0742 result_.ex_ = &ex;
0743 result_.value_ = &value;
0744 this->resume();
0745 }
0746
0747 static return_type on_resume(result_type& result)
0748 {
0749 if (*result.ex_)
0750 rethrow_exception(*result.ex_);
0751 return static_cast<return_type&&>(*result.value_);
0752 }
0753
0754 private:
0755 result_type& result_;
0756 };
0757
0758 template <typename Executor, typename Signature>
0759 inline bool asio_handler_is_continuation(spawn_handler<Executor, Signature>*)
0760 {
0761 return true;
0762 }
0763
0764 }
0765
0766 template <typename Executor, typename Signature>
0767 class async_result<basic_yield_context<Executor>, Signature>
0768 {
0769 public:
0770 typedef typename detail::spawn_handler<Executor, Signature> handler_type;
0771 typedef typename handler_type::return_type return_type;
0772
0773 #if defined(BOOST_ASIO_HAS_VARIADIC_LAMBDA_CAPTURES)
0774
0775 template <typename Initiation, typename... InitArgs>
0776 static return_type initiate(Initiation&& init,
0777 const basic_yield_context<Executor>& yield,
0778 InitArgs&&... init_args)
0779 {
0780 typename handler_type::result_type result
0781 = typename handler_type::result_type();
0782
0783 yield.spawned_thread_->suspend_with(
0784 [&]()
0785 {
0786 static_cast<Initiation&&>(init)(
0787 handler_type(yield, result),
0788 static_cast<InitArgs&&>(init_args)...);
0789 });
0790
0791 return handler_type::on_resume(result);
0792 }
0793
0794 #else
0795
0796 template <typename Initiation, typename... InitArgs>
0797 struct suspend_with_helper
0798 {
0799 typename handler_type::result_type& result_;
0800 Initiation&& init_;
0801 const basic_yield_context<Executor>& yield_;
0802 std::tuple<InitArgs&&...> init_args_;
0803
0804 template <std::size_t... I>
0805 void do_invoke(detail::index_sequence<I...>)
0806 {
0807 static_cast<Initiation&&>(init_)(
0808 handler_type(yield_, result_),
0809 static_cast<InitArgs&&>(std::get<I>(init_args_))...);
0810 }
0811
0812 void operator()()
0813 {
0814 this->do_invoke(detail::make_index_sequence<sizeof...(InitArgs)>());
0815 }
0816 };
0817
0818 template <typename Initiation, typename... InitArgs>
0819 static return_type initiate(Initiation&& init,
0820 const basic_yield_context<Executor>& yield,
0821 InitArgs&&... init_args)
0822 {
0823 typename handler_type::result_type result
0824 = typename handler_type::result_type();
0825
0826 yield.spawned_thread_->suspend_with(
0827 suspend_with_helper<Initiation, InitArgs...>{
0828 result, static_cast<Initiation&&>(init), yield,
0829 std::tuple<InitArgs&&...>(
0830 static_cast<InitArgs&&>(init_args)...)});
0831
0832 return handler_type::on_resume(result);
0833 }
0834
0835 #endif
0836 };
0837
0838 namespace detail {
0839
0840 template <typename Executor, typename Function, typename Handler>
0841 class spawn_entry_point
0842 {
0843 public:
0844 template <typename F, typename H>
0845 spawn_entry_point(const Executor& ex,
0846 F&& f, H&& h)
0847 : executor_(ex),
0848 function_(static_cast<F&&>(f)),
0849 handler_(static_cast<H&&>(h)),
0850 work_(handler_, executor_)
0851 {
0852 }
0853
0854 void operator()(spawned_thread_base* spawned_thread)
0855 {
0856 const basic_yield_context<Executor> yield(spawned_thread, executor_);
0857 this->call(yield,
0858 void_type<result_of_t<Function(basic_yield_context<Executor>)>>());
0859 }
0860
0861 private:
0862 void call(const basic_yield_context<Executor>& yield, void_type<void>)
0863 {
0864 #if !defined(BOOST_ASIO_NO_EXCEPTIONS)
0865 try
0866 #endif
0867 {
0868 function_(yield);
0869 if (!yield.spawned_thread_->has_context_switched())
0870 (post)(yield);
0871 detail::binder1<Handler, exception_ptr>
0872 handler(handler_, exception_ptr());
0873 work_.complete(handler, handler.handler_);
0874 }
0875 #if !defined(BOOST_ASIO_NO_EXCEPTIONS)
0876 # if defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
0877 catch (const boost::context::detail::forced_unwind&)
0878 {
0879 throw;
0880 }
0881 # endif
0882 # if defined(BOOST_ASIO_HAS_BOOST_COROUTINE)
0883 catch (const boost::coroutines::detail::forced_unwind&)
0884 {
0885 throw;
0886 }
0887 # endif
0888 catch (...)
0889 {
0890 exception_ptr ex = current_exception();
0891 if (!yield.spawned_thread_->has_context_switched())
0892 (post)(yield);
0893 detail::binder1<Handler, exception_ptr> handler(handler_, ex);
0894 work_.complete(handler, handler.handler_);
0895 }
0896 #endif
0897 }
0898
0899 template <typename T>
0900 void call(const basic_yield_context<Executor>& yield, void_type<T>)
0901 {
0902 #if !defined(BOOST_ASIO_NO_EXCEPTIONS)
0903 try
0904 #endif
0905 {
0906 T result(function_(yield));
0907 if (!yield.spawned_thread_->has_context_switched())
0908 (post)(yield);
0909 detail::binder2<Handler, exception_ptr, T>
0910 handler(handler_, exception_ptr(), static_cast<T&&>(result));
0911 work_.complete(handler, handler.handler_);
0912 }
0913 #if !defined(BOOST_ASIO_NO_EXCEPTIONS)
0914 # if defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
0915 catch (const boost::context::detail::forced_unwind&)
0916 {
0917 throw;
0918 }
0919 # endif
0920 # if defined(BOOST_ASIO_HAS_BOOST_COROUTINE)
0921 catch (const boost::coroutines::detail::forced_unwind&)
0922 {
0923 throw;
0924 }
0925 # endif
0926 catch (...)
0927 {
0928 exception_ptr ex = current_exception();
0929 if (!yield.spawned_thread_->has_context_switched())
0930 (post)(yield);
0931 detail::binder2<Handler, exception_ptr, T> handler(handler_, ex, T());
0932 work_.complete(handler, handler.handler_);
0933 }
0934 #endif
0935 }
0936
0937 Executor executor_;
0938 Function function_;
0939 Handler handler_;
0940 handler_work<Handler, Executor> work_;
0941 };
0942
0943 struct spawn_cancellation_signal_emitter
0944 {
0945 cancellation_signal* signal_;
0946 cancellation_type_t type_;
0947
0948 void operator()()
0949 {
0950 signal_->emit(type_);
0951 }
0952 };
0953
0954 template <typename Handler, typename Executor, typename = void>
0955 class spawn_cancellation_handler
0956 {
0957 public:
0958 spawn_cancellation_handler(const Handler&, const Executor& ex)
0959 : ex_(ex)
0960 {
0961 }
0962
0963 cancellation_slot slot()
0964 {
0965 return signal_.slot();
0966 }
0967
0968 void operator()(cancellation_type_t type)
0969 {
0970 spawn_cancellation_signal_emitter emitter = { &signal_, type };
0971 (dispatch)(ex_, emitter);
0972 }
0973
0974 private:
0975 cancellation_signal signal_;
0976 Executor ex_;
0977 };
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