Back to home page

EIC code displayed by LXR

 
 

    


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 // impl/spawn.hpp
0003 // ~~~~~~~~~~~~~~
0004 //
0005 // Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com)
0006 //
0007 // Distributed under the Boost Software License, Version 1.0. (See accompanying
0008 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
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 // defined(_MSC_VER) && (_MSC_VER >= 1200)
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 // defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
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 // !defined(BOOST_ASIO_NO_EXCEPTIONS)
0052 
0053 #if defined(BOOST_ASIO_HAS_BOOST_COROUTINE)
0054 
0055 // Spawned thread implementation using Boost.Coroutine.
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 // !defined(BOOST_ASIO_NO_EXCEPTIONS)
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 // !defined(BOOST_ASIO_NO_EXCEPTIONS)
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 // defined(BOOST_ASIO_HAS_BOOST_COROUTINE)
0183 
0184 #if defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
0185 
0186 // Spawned thread implementation using Boost.Context's fiber.
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 // !defined(BOOST_ASIO_NO_EXCEPTIONS)
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 // !defined(BOOST_ASIO_NO_EXCEPTIONS)
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 // defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
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 // Helper class to perform the initial resume on the correct executor.
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 // Helper class to ensure spawned threads are destroyed on the correct executor.
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 // Base class for all completion handlers associated with a spawned thread.
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 // Completion handlers for when basic_yield_context is used as a token.
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 } // namespace detail
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 // defined(BOOST_ASIO_HAS_VARIADIC_LAMBDA_CAPTURES)
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 // defined(BOOST_ASIO_HAS_VARIADIC_LAMBDA_CAPTURES)
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 // !defined(BOOST_ASIO_NO_EXCEPTIONS)
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 // defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
0883 # if defined(BOOST_ASIO_HAS_BOOST_COROUTINE)
0884     catch (const boost::coroutines::detail::forced_unwind&)
0885     {
0886       throw;
0887     }
0888 # endif // defined(BOOST_ASIO_HAS_BOOST_COROUTINE)
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 // !defined(BOOST_ASIO_NO_EXCEPTIONS)
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 // !defined(BOOST_ASIO_NO_EXCEPTIONS)
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 // defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
0921 # if defined(BOOST_ASIO_HAS_BOOST_COROUTINE)
0922     catch (const boost::coroutines::detail::forced_unwind&)
0923     {
0924       throw;
0925     }
0926 # endif // defined(BOOST_ASIO_HAS_BOOST_COROUTINE)
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 // !defined(BOOST_ASIO_NO_EXCEPTIONS)
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 // defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
1094 
1095 private:
1096   executor_type executor_;
1097 };
1098 
1099 } // namespace detail
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 // defined(BOOST_ASIO_HAS_BOOST_COROUTINE)
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 // defined(BOOST_ASIO_HAS_BOOST_COROUTINE)
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 // defined(BOOST_ASIO_HAS_BOOST_COROUTINE)
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 // defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
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 } // namespace detail
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 // !defined(BOOST_ASIO_NO_TS_EXECUTORS)
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 // defined(BOOST_ASIO_HAS_BOOST_COROUTINE)
1396 
1397 } // namespace asio
1398 } // namespace boost
1399 
1400 #include <boost/asio/detail/pop_options.hpp>
1401 
1402 #endif // BOOST_ASIO_IMPL_SPAWN_HPP