Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:28:57

0001 //
0002 // impl/spawn.hpp
0003 // ~~~~~~~~~~~~~~
0004 //
0005 // Copyright (c) 2003-2023 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/system/system_error.hpp>
0033 
0034 #if defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
0035 # include <boost/context/fiber.hpp>
0036 #endif // defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
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 // !defined(BOOST_ASIO_NO_EXCEPTIONS)
0051 
0052 #if defined(BOOST_ASIO_HAS_BOOST_COROUTINE)
0053 
0054 // Spawned thread implementation using Boost.Coroutine.
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 // !defined(BOOST_ASIO_NO_EXCEPTIONS)
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 // !defined(BOOST_ASIO_NO_EXCEPTIONS)
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 // defined(BOOST_ASIO_HAS_BOOST_COROUTINE)
0182 
0183 #if defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
0184 
0185 // Spawned thread implementation using Boost.Context's fiber.
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 // !defined(BOOST_ASIO_NO_EXCEPTIONS)
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 // !defined(BOOST_ASIO_NO_EXCEPTIONS)
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 // defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
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 // Helper class to perform the initial resume on the correct executor.
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 // Helper class to ensure spawned threads are destroyed on the correct executor.
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 // Base class for all completion handlers associated with a spawned thread.
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 // Completion handlers for when basic_yield_context is used as a token.
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 } // namespace detail
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 // defined(BOOST_ASIO_HAS_VARIADIC_LAMBDA_CAPTURES)
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 // defined(BOOST_ASIO_HAS_VARIADIC_LAMBDA_CAPTURES)
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 // !defined(BOOST_ASIO_NO_EXCEPTIONS)
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 // defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
0882 # if defined(BOOST_ASIO_HAS_BOOST_COROUTINE)
0883     catch (const boost::coroutines::detail::forced_unwind&)
0884     {
0885       throw;
0886     }
0887 # endif // defined(BOOST_ASIO_HAS_BOOST_COROUTINE)
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 // !defined(BOOST_ASIO_NO_EXCEPTIONS)
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 // !defined(BOOST_ASIO_NO_EXCEPTIONS)
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 // defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
0920 # if defined(BOOST_ASIO_HAS_BOOST_COROUTINE)
0921     catch (const boost::coroutines::detail::forced_unwind&)
0922     {
0923       throw;
0924     }
0925 # endif // defined(BOOST_ASIO_HAS_BOOST_COROUTINE)
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 // !defined(BOOST_ASIO_NO_EXCEPTIONS)
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 // 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