// // impl/spawn.hpp // ~~~~~~~~~~~~~~ // // Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef BOOST_ASIO_IMPL_SPAWN_HPP #define BOOST_ASIO_IMPL_SPAWN_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER) # include #endif // defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER) #include namespace boost { namespace asio { namespace detail { #if !defined(BOOST_ASIO_NO_EXCEPTIONS) inline void spawned_thread_rethrow(void* ex) { if (*static_cast(ex)) rethrow_exception(*static_cast(ex)); } #endif // !defined(BOOST_ASIO_NO_EXCEPTIONS) #if defined(BOOST_ASIO_HAS_BOOST_COROUTINE) // Spawned thread implementation using Boost.Coroutine. class spawned_coroutine_thread : public spawned_thread_base { public: #if defined(BOOST_COROUTINES_UNIDIRECT) || defined(BOOST_COROUTINES_V2) typedef boost::coroutines::pull_coroutine callee_type; typedef boost::coroutines::push_coroutine caller_type; #else typedef boost::coroutines::coroutine callee_type; typedef boost::coroutines::coroutine caller_type; #endif spawned_coroutine_thread(caller_type& caller) : caller_(caller), on_suspend_fn_(0), on_suspend_arg_(0) { } template static spawned_thread_base* spawn(F&& f, const boost::coroutines::attributes& attributes, cancellation_slot parent_cancel_slot = cancellation_slot(), cancellation_state cancel_state = cancellation_state()) { spawned_coroutine_thread* spawned_thread = 0; callee_type callee(entry_point>( static_cast(f), &spawned_thread), attributes); spawned_thread->callee_.swap(callee); spawned_thread->parent_cancellation_slot_ = parent_cancel_slot; spawned_thread->cancellation_state_ = cancel_state; return spawned_thread; } template static spawned_thread_base* spawn(F&& f, cancellation_slot parent_cancel_slot = cancellation_slot(), cancellation_state cancel_state = cancellation_state()) { return spawn(static_cast(f), boost::coroutines::attributes(), parent_cancel_slot, cancel_state); } void resume() { callee_(); if (on_suspend_fn_) { void (*fn)(void*) = on_suspend_fn_; void* arg = on_suspend_arg_; on_suspend_fn_ = 0; fn(arg); } } void suspend_with(void (*fn)(void*), void* arg) { if (throw_if_cancelled_) if (!!cancellation_state_.cancelled()) throw_error(boost::asio::error::operation_aborted, "yield"); has_context_switched_ = true; on_suspend_fn_ = fn; on_suspend_arg_ = arg; caller_(); } void destroy() { callee_type callee; callee.swap(callee_); if (terminal_) callee(); } private: template class entry_point { public: template entry_point(F&& f, spawned_coroutine_thread** spawned_thread_out) : function_(static_cast(f)), spawned_thread_out_(spawned_thread_out) { } void operator()(caller_type& caller) { Function function(static_cast(function_)); spawned_coroutine_thread spawned_thread(caller); *spawned_thread_out_ = &spawned_thread; spawned_thread_out_ = 0; spawned_thread.suspend(); #if !defined(BOOST_ASIO_NO_EXCEPTIONS) try #endif // !defined(BOOST_ASIO_NO_EXCEPTIONS) { function(&spawned_thread); spawned_thread.terminal_ = true; spawned_thread.suspend(); } #if !defined(BOOST_ASIO_NO_EXCEPTIONS) catch (const boost::coroutines::detail::forced_unwind&) { throw; } catch (...) { exception_ptr ex = current_exception(); spawned_thread.terminal_ = true; spawned_thread.suspend_with(spawned_thread_rethrow, &ex); } #endif // !defined(BOOST_ASIO_NO_EXCEPTIONS) } private: Function function_; spawned_coroutine_thread** spawned_thread_out_; }; caller_type& caller_; callee_type callee_; void (*on_suspend_fn_)(void*); void* on_suspend_arg_; }; #endif // defined(BOOST_ASIO_HAS_BOOST_COROUTINE) #if defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER) // Spawned thread implementation using Boost.Context's fiber. class spawned_fiber_thread : public spawned_thread_base { public: typedef boost::context::fiber fiber_type; spawned_fiber_thread(fiber_type&& caller) : caller_(static_cast(caller)), on_suspend_fn_(0), on_suspend_arg_(0) { } template static spawned_thread_base* spawn(allocator_arg_t, StackAllocator&& stack_allocator, F&& f, cancellation_slot parent_cancel_slot = cancellation_slot(), cancellation_state cancel_state = cancellation_state()) { spawned_fiber_thread* spawned_thread = 0; fiber_type callee(allocator_arg_t(), static_cast(stack_allocator), entry_point>( static_cast(f), &spawned_thread)); callee = fiber_type(static_cast(callee)).resume(); spawned_thread->callee_ = static_cast(callee); spawned_thread->parent_cancellation_slot_ = parent_cancel_slot; spawned_thread->cancellation_state_ = cancel_state; return spawned_thread; } template static spawned_thread_base* spawn(F&& f, cancellation_slot parent_cancel_slot = cancellation_slot(), cancellation_state cancel_state = cancellation_state()) { return spawn(allocator_arg_t(), boost::context::fixedsize_stack(), static_cast(f), parent_cancel_slot, cancel_state); } void resume() { callee_ = fiber_type(static_cast(callee_)).resume(); if (on_suspend_fn_) { void (*fn)(void*) = on_suspend_fn_; void* arg = on_suspend_arg_; on_suspend_fn_ = 0; fn(arg); } } void suspend_with(void (*fn)(void*), void* arg) { if (throw_if_cancelled_) if (!!cancellation_state_.cancelled()) throw_error(boost::asio::error::operation_aborted, "yield"); has_context_switched_ = true; on_suspend_fn_ = fn; on_suspend_arg_ = arg; caller_ = fiber_type(static_cast(caller_)).resume(); } void destroy() { fiber_type callee = static_cast(callee_); if (terminal_) fiber_type(static_cast(callee)).resume(); } private: template class entry_point { public: template entry_point(F&& f, spawned_fiber_thread** spawned_thread_out) : function_(static_cast(f)), spawned_thread_out_(spawned_thread_out) { } fiber_type operator()(fiber_type&& caller) { Function function(static_cast(function_)); spawned_fiber_thread spawned_thread( static_cast(caller)); *spawned_thread_out_ = &spawned_thread; spawned_thread_out_ = 0; spawned_thread.suspend(); #if !defined(BOOST_ASIO_NO_EXCEPTIONS) try #endif // !defined(BOOST_ASIO_NO_EXCEPTIONS) { function(&spawned_thread); spawned_thread.terminal_ = true; spawned_thread.suspend(); } #if !defined(BOOST_ASIO_NO_EXCEPTIONS) catch (const boost::context::detail::forced_unwind&) { throw; } catch (...) { exception_ptr ex = current_exception(); spawned_thread.terminal_ = true; spawned_thread.suspend_with(spawned_thread_rethrow, &ex); } #endif // !defined(BOOST_ASIO_NO_EXCEPTIONS) return static_cast(spawned_thread.caller_); } private: Function function_; spawned_fiber_thread** spawned_thread_out_; }; fiber_type caller_; fiber_type callee_; void (*on_suspend_fn_)(void*); void* on_suspend_arg_; }; #endif // defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER) #if defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER) typedef spawned_fiber_thread default_spawned_thread_type; #elif defined(BOOST_ASIO_HAS_BOOST_COROUTINE) typedef spawned_coroutine_thread default_spawned_thread_type; #else # error No spawn() implementation available #endif // Helper class to perform the initial resume on the correct executor. class spawned_thread_resumer { public: explicit spawned_thread_resumer(spawned_thread_base* spawned_thread) : spawned_thread_(spawned_thread) { } spawned_thread_resumer(spawned_thread_resumer&& other) noexcept : spawned_thread_(other.spawned_thread_) { other.spawned_thread_ = 0; } ~spawned_thread_resumer() { if (spawned_thread_) spawned_thread_->destroy(); } void operator()() { spawned_thread_->attach(&spawned_thread_); spawned_thread_->resume(); } private: spawned_thread_base* spawned_thread_; }; // Helper class to ensure spawned threads are destroyed on the correct executor. class spawned_thread_destroyer { public: explicit spawned_thread_destroyer(spawned_thread_base* spawned_thread) : spawned_thread_(spawned_thread) { spawned_thread->detach(); } spawned_thread_destroyer(spawned_thread_destroyer&& other) noexcept : spawned_thread_(other.spawned_thread_) { other.spawned_thread_ = 0; } ~spawned_thread_destroyer() { if (spawned_thread_) spawned_thread_->destroy(); } void operator()() { if (spawned_thread_) { spawned_thread_->destroy(); spawned_thread_ = 0; } } private: spawned_thread_base* spawned_thread_; }; // Base class for all completion handlers associated with a spawned thread. template class spawn_handler_base { public: typedef Executor executor_type; typedef cancellation_slot cancellation_slot_type; spawn_handler_base(const basic_yield_context& yield) : yield_(yield), spawned_thread_(yield.spawned_thread_) { spawned_thread_->detach(); } spawn_handler_base(spawn_handler_base&& other) noexcept : yield_(other.yield_), spawned_thread_(other.spawned_thread_) { other.spawned_thread_ = 0; } ~spawn_handler_base() { if (spawned_thread_) (post)(yield_.executor_, spawned_thread_destroyer(spawned_thread_)); } executor_type get_executor() const noexcept { return yield_.executor_; } cancellation_slot_type get_cancellation_slot() const noexcept { return spawned_thread_->get_cancellation_slot(); } void resume() { spawned_thread_resumer resumer(spawned_thread_); spawned_thread_ = 0; resumer(); } protected: const basic_yield_context& yield_; spawned_thread_base* spawned_thread_; }; // Completion handlers for when basic_yield_context is used as a token. template class spawn_handler; template class spawn_handler : public spawn_handler_base { public: typedef void return_type; struct result_type {}; spawn_handler(const basic_yield_context& yield, result_type&) : spawn_handler_base(yield) { } void operator()() { this->resume(); } static return_type on_resume(result_type&) { } }; template class spawn_handler : public spawn_handler_base { public: typedef void return_type; typedef boost::system::error_code* result_type; spawn_handler(const basic_yield_context& yield, result_type& result) : spawn_handler_base(yield), result_(result) { } void operator()(boost::system::error_code ec) { if (this->yield_.ec_) { *this->yield_.ec_ = ec; result_ = 0; } else result_ = &ec; this->resume(); } static return_type on_resume(result_type& result) { if (result) throw_error(*result); } private: result_type& result_; }; template class spawn_handler : public spawn_handler_base { public: typedef void return_type; typedef exception_ptr* result_type; spawn_handler(const basic_yield_context& yield, result_type& result) : spawn_handler_base(yield), result_(result) { } void operator()(exception_ptr ex) { result_ = &ex; this->resume(); } static return_type on_resume(result_type& result) { if (*result) rethrow_exception(*result); } private: result_type& result_; }; template class spawn_handler : public spawn_handler_base { public: typedef T return_type; typedef return_type* result_type; spawn_handler(const basic_yield_context& yield, result_type& result) : spawn_handler_base(yield), result_(result) { } void operator()(T value) { result_ = &value; this->resume(); } static return_type on_resume(result_type& result) { return static_cast(*result); } private: result_type& result_; }; template class spawn_handler : public spawn_handler_base { public: typedef T return_type; struct result_type { boost::system::error_code* ec_; return_type* value_; }; spawn_handler(const basic_yield_context& yield, result_type& result) : spawn_handler_base(yield), result_(result) { } void operator()(boost::system::error_code ec, T value) { if (this->yield_.ec_) { *this->yield_.ec_ = ec; result_.ec_ = 0; } else result_.ec_ = &ec; result_.value_ = &value; this->resume(); } static return_type on_resume(result_type& result) { if (result.ec_) throw_error(*result.ec_); return static_cast(*result.value_); } private: result_type& result_; }; template class spawn_handler : public spawn_handler_base { public: typedef T return_type; struct result_type { exception_ptr* ex_; return_type* value_; }; spawn_handler(const basic_yield_context& yield, result_type& result) : spawn_handler_base(yield), result_(result) { } void operator()(exception_ptr ex, T value) { result_.ex_ = &ex; result_.value_ = &value; this->resume(); } static return_type on_resume(result_type& result) { if (*result.ex_) rethrow_exception(*result.ex_); return static_cast(*result.value_); } private: result_type& result_; }; template class spawn_handler : public spawn_handler_base { public: typedef std::tuple return_type; typedef return_type* result_type; spawn_handler(const basic_yield_context& yield, result_type& result) : spawn_handler_base(yield), result_(result) { } template void operator()(Args&&... args) { return_type value(static_cast(args)...); result_ = &value; this->resume(); } static return_type on_resume(result_type& result) { return static_cast(*result); } private: result_type& result_; }; template class spawn_handler : public spawn_handler_base { public: typedef std::tuple return_type; struct result_type { boost::system::error_code* ec_; return_type* value_; }; spawn_handler(const basic_yield_context& yield, result_type& result) : spawn_handler_base(yield), result_(result) { } template void operator()(boost::system::error_code ec, Args&&... args) { return_type value(static_cast(args)...); if (this->yield_.ec_) { *this->yield_.ec_ = ec; result_.ec_ = 0; } else result_.ec_ = &ec; result_.value_ = &value; this->resume(); } static return_type on_resume(result_type& result) { if (result.ec_) throw_error(*result.ec_); return static_cast(*result.value_); } private: result_type& result_; }; template class spawn_handler : public spawn_handler_base { public: typedef std::tuple return_type; struct result_type { exception_ptr* ex_; return_type* value_; }; spawn_handler(const basic_yield_context& yield, result_type& result) : spawn_handler_base(yield), result_(result) { } template void operator()(exception_ptr ex, Args&&... args) { return_type value(static_cast(args)...); result_.ex_ = &ex; result_.value_ = &value; this->resume(); } static return_type on_resume(result_type& result) { if (*result.ex_) rethrow_exception(*result.ex_); return static_cast(*result.value_); } private: result_type& result_; }; template inline bool asio_handler_is_continuation(spawn_handler*) { return true; } } // namespace detail template class async_result, Signature> { public: typedef typename detail::spawn_handler handler_type; typedef typename handler_type::return_type return_type; #if defined(BOOST_ASIO_HAS_VARIADIC_LAMBDA_CAPTURES) template static return_type initiate(Initiation&& init, const basic_yield_context& yield, InitArgs&&... init_args) { typename handler_type::result_type result = typename handler_type::result_type(); yield.spawned_thread_->suspend_with( [&]() { static_cast(init)( handler_type(yield, result), static_cast(init_args)...); }); return handler_type::on_resume(result); } #else // defined(BOOST_ASIO_HAS_VARIADIC_LAMBDA_CAPTURES) template struct suspend_with_helper { typename handler_type::result_type& result_; Initiation&& init_; const basic_yield_context& yield_; std::tuple init_args_; template void do_invoke(detail::index_sequence) { static_cast(init_)( handler_type(yield_, result_), static_cast(std::get(init_args_))...); } void operator()() { this->do_invoke(detail::make_index_sequence()); } }; template static return_type initiate(Initiation&& init, const basic_yield_context& yield, InitArgs&&... init_args) { typename handler_type::result_type result = typename handler_type::result_type(); yield.spawned_thread_->suspend_with( suspend_with_helper{ result, static_cast(init), yield, std::tuple( static_cast(init_args)...)}); return handler_type::on_resume(result); } #endif // defined(BOOST_ASIO_HAS_VARIADIC_LAMBDA_CAPTURES) }; namespace detail { template class spawn_entry_point { public: template spawn_entry_point(const Executor& ex, F&& f, H&& h) : executor_(ex), function_(static_cast(f)), handler_(static_cast(h)), work_(handler_, executor_) { } void operator()(spawned_thread_base* spawned_thread) { const basic_yield_context yield(spawned_thread, executor_); this->call(yield, void_type)>>()); } private: void call(const basic_yield_context& yield, void_type) { #if !defined(BOOST_ASIO_NO_EXCEPTIONS) try #endif // !defined(BOOST_ASIO_NO_EXCEPTIONS) { function_(yield); if (!yield.spawned_thread_->has_context_switched()) (post)(yield); detail::binder1 handler(handler_, exception_ptr()); work_.complete(handler, handler.handler_); } #if !defined(BOOST_ASIO_NO_EXCEPTIONS) # if defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER) catch (const boost::context::detail::forced_unwind&) { throw; } # endif // defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER) # if defined(BOOST_ASIO_HAS_BOOST_COROUTINE) catch (const boost::coroutines::detail::forced_unwind&) { throw; } # endif // defined(BOOST_ASIO_HAS_BOOST_COROUTINE) catch (...) { exception_ptr ex = current_exception(); if (!yield.spawned_thread_->has_context_switched()) (post)(yield); detail::binder1 handler(handler_, ex); work_.complete(handler, handler.handler_); } #endif // !defined(BOOST_ASIO_NO_EXCEPTIONS) } template void call(const basic_yield_context& yield, void_type) { #if !defined(BOOST_ASIO_NO_EXCEPTIONS) try #endif // !defined(BOOST_ASIO_NO_EXCEPTIONS) { T result(function_(yield)); if (!yield.spawned_thread_->has_context_switched()) (post)(yield); detail::binder2 handler(handler_, exception_ptr(), static_cast(result)); work_.complete(handler, handler.handler_); } #if !defined(BOOST_ASIO_NO_EXCEPTIONS) # if defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER) catch (const boost::context::detail::forced_unwind&) { throw; } # endif // defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER) # if defined(BOOST_ASIO_HAS_BOOST_COROUTINE) catch (const boost::coroutines::detail::forced_unwind&) { throw; } # endif // defined(BOOST_ASIO_HAS_BOOST_COROUTINE) catch (...) { exception_ptr ex = current_exception(); if (!yield.spawned_thread_->has_context_switched()) (post)(yield); detail::binder2 handler(handler_, ex, T()); work_.complete(handler, handler.handler_); } #endif // !defined(BOOST_ASIO_NO_EXCEPTIONS) } Executor executor_; Function function_; Handler handler_; handler_work work_; }; struct spawn_cancellation_signal_emitter { cancellation_signal* signal_; cancellation_type_t type_; void operator()() { signal_->emit(type_); } }; template class spawn_cancellation_handler { public: spawn_cancellation_handler(const Handler&, const Executor& ex) : ex_(ex) { } cancellation_slot slot() { return signal_.slot(); } void operator()(cancellation_type_t type) { spawn_cancellation_signal_emitter emitter = { &signal_, type }; (dispatch)(ex_, emitter); } private: cancellation_signal signal_; Executor ex_; }; template class spawn_cancellation_handler::asio_associated_executor_is_unspecialised, void >::value >> { public: spawn_cancellation_handler(const Handler&, const Executor&) { } cancellation_slot slot() { return signal_.slot(); } void operator()(cancellation_type_t type) { signal_.emit(type); } private: cancellation_signal signal_; }; template class initiate_spawn { public: typedef Executor executor_type; explicit initiate_spawn(const executor_type& ex) : executor_(ex) { } executor_type get_executor() const noexcept { return executor_; } template void operator()(Handler&& handler, F&& f) const { typedef decay_t handler_type; typedef decay_t function_type; typedef spawn_cancellation_handler< handler_type, Executor> cancel_handler_type; associated_cancellation_slot_t slot = boost::asio::get_associated_cancellation_slot(handler); cancel_handler_type* cancel_handler = slot.is_connected() ? &slot.template emplace(handler, executor_) : 0; cancellation_slot proxy_slot( cancel_handler ? cancel_handler->slot() : cancellation_slot()); cancellation_state cancel_state(proxy_slot); (dispatch)(executor_, spawned_thread_resumer( default_spawned_thread_type::spawn( spawn_entry_point( executor_, static_cast(f), static_cast(handler)), proxy_slot, cancel_state))); } #if defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER) template void operator()(Handler&& handler, allocator_arg_t, StackAllocator&& stack_allocator, F&& f) const { typedef decay_t handler_type; typedef decay_t function_type; typedef spawn_cancellation_handler< handler_type, Executor> cancel_handler_type; associated_cancellation_slot_t slot = boost::asio::get_associated_cancellation_slot(handler); cancel_handler_type* cancel_handler = slot.is_connected() ? &slot.template emplace(handler, executor_) : 0; cancellation_slot proxy_slot( cancel_handler ? cancel_handler->slot() : cancellation_slot()); cancellation_state cancel_state(proxy_slot); (dispatch)(executor_, spawned_thread_resumer( spawned_fiber_thread::spawn(allocator_arg_t(), static_cast(stack_allocator), spawn_entry_point( executor_, static_cast(f), static_cast(handler)), proxy_slot, cancel_state))); } #endif // defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER) private: executor_type executor_; }; } // namespace detail template )>>::type) CompletionToken> inline auto spawn(const Executor& ex, F&& function, CompletionToken&& token, #if defined(BOOST_ASIO_HAS_BOOST_COROUTINE) constraint_t< !is_same< decay_t, boost::coroutines::attributes >::value >, #endif // defined(BOOST_ASIO_HAS_BOOST_COROUTINE) constraint_t< is_executor::value || execution::is_executor::value >) -> decltype( async_initiate)>>::type>( declval>(), token, static_cast(function))) { return async_initiate)>>::type>( detail::initiate_spawn(ex), token, static_cast(function)); } template )>>::type) CompletionToken> inline auto spawn(ExecutionContext& ctx, F&& function, CompletionToken&& token, #if defined(BOOST_ASIO_HAS_BOOST_COROUTINE) constraint_t< !is_same< decay_t, boost::coroutines::attributes >::value >, #endif // defined(BOOST_ASIO_HAS_BOOST_COROUTINE) constraint_t< is_convertible::value >) -> decltype( async_initiate)>>::type>( declval>(), token, static_cast(function))) { return (spawn)(ctx.get_executor(), static_cast(function), static_cast(token)); } template )>>::type) CompletionToken> inline auto spawn(const basic_yield_context& ctx, F&& function, CompletionToken&& token, #if defined(BOOST_ASIO_HAS_BOOST_COROUTINE) constraint_t< !is_same< decay_t, boost::coroutines::attributes >::value >, #endif // defined(BOOST_ASIO_HAS_BOOST_COROUTINE) constraint_t< is_executor::value || execution::is_executor::value >) -> decltype( async_initiate)>>::type>( declval>(), token, static_cast(function))) { return (spawn)(ctx.get_executor(), static_cast(function), static_cast(token)); } #if defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER) template )>>::type) CompletionToken> inline auto spawn(const Executor& ex, allocator_arg_t, StackAllocator&& stack_allocator, F&& function, CompletionToken&& token, constraint_t< is_executor::value || execution::is_executor::value >) -> decltype( async_initiate)>>::type>( declval>(), token, allocator_arg_t(), static_cast(stack_allocator), static_cast(function))) { return async_initiate)>>::type>( detail::initiate_spawn(ex), token, allocator_arg_t(), static_cast(stack_allocator), static_cast(function)); } template )>>::type) CompletionToken> inline auto spawn(ExecutionContext& ctx, allocator_arg_t, StackAllocator&& stack_allocator, F&& function, CompletionToken&& token, constraint_t< is_convertible::value >) -> decltype( async_initiate)>>::type>( declval>(), token, allocator_arg_t(), static_cast(stack_allocator), static_cast(function))) { return (spawn)(ctx.get_executor(), allocator_arg_t(), static_cast(stack_allocator), static_cast(function), static_cast(token)); } template )>>::type) CompletionToken> inline auto spawn(const basic_yield_context& ctx, allocator_arg_t, StackAllocator&& stack_allocator, F&& function, CompletionToken&& token, constraint_t< is_executor::value || execution::is_executor::value >) -> decltype( async_initiate)>>::type>( declval>(), token, allocator_arg_t(), static_cast(stack_allocator), static_cast(function))) { return (spawn)(ctx.get_executor(), allocator_arg_t(), static_cast(stack_allocator), static_cast(function), static_cast(token)); } #endif // defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER) #if defined(BOOST_ASIO_HAS_BOOST_COROUTINE) namespace detail { template class old_spawn_entry_point { public: template old_spawn_entry_point(const Executor& ex, F&& f, H&& h) : executor_(ex), function_(static_cast(f)), handler_(static_cast(h)) { } void operator()(spawned_thread_base* spawned_thread) { const basic_yield_context yield(spawned_thread, executor_); this->call(yield, void_type)>>()); } private: void call(const basic_yield_context& yield, void_type) { function_(yield); static_cast(handler_)(); } template void call(const basic_yield_context& yield, void_type) { static_cast(handler_)(function_(yield)); } Executor executor_; Function function_; Handler handler_; }; inline void default_spawn_handler() {} } // namespace detail template inline void spawn(Function&& function, const boost::coroutines::attributes& attributes) { associated_executor_t> ex( (get_associated_executor)(function)); boost::asio::spawn(ex, static_cast(function), attributes); } template void spawn(Handler&& handler, Function&& function, const boost::coroutines::attributes& attributes, constraint_t< !is_executor>::value && !execution::is_executor>::value && !is_convertible::value>) { typedef associated_executor_t> executor_type; executor_type ex((get_associated_executor)(handler)); (dispatch)(ex, detail::spawned_thread_resumer( detail::spawned_coroutine_thread::spawn( detail::old_spawn_entry_point, void (*)()>( ex, static_cast(function), &detail::default_spawn_handler), attributes))); } template void spawn(basic_yield_context ctx, Function&& function, const boost::coroutines::attributes& attributes) { (dispatch)(ctx.get_executor(), detail::spawned_thread_resumer( detail::spawned_coroutine_thread::spawn( detail::old_spawn_entry_point, void (*)()>( ctx.get_executor(), static_cast(function), &detail::default_spawn_handler), attributes))); } template inline void spawn(const Executor& ex, Function&& function, const boost::coroutines::attributes& attributes, constraint_t< is_executor::value || execution::is_executor::value >) { boost::asio::spawn(boost::asio::strand(ex), static_cast(function), attributes); } template inline void spawn(const strand& ex, Function&& function, const boost::coroutines::attributes& attributes) { boost::asio::spawn(boost::asio::bind_executor( ex, &detail::default_spawn_handler), static_cast(function), attributes); } #if !defined(BOOST_ASIO_NO_TS_EXECUTORS) template inline void spawn(const boost::asio::io_context::strand& s, Function&& function, const boost::coroutines::attributes& attributes) { boost::asio::spawn(boost::asio::bind_executor( s, &detail::default_spawn_handler), static_cast(function), attributes); } #endif // !defined(BOOST_ASIO_NO_TS_EXECUTORS) template inline void spawn(ExecutionContext& ctx, Function&& function, const boost::coroutines::attributes& attributes, constraint_t< is_convertible::value >) { boost::asio::spawn(ctx.get_executor(), static_cast(function), attributes); } #endif // defined(BOOST_ASIO_HAS_BOOST_COROUTINE) } // namespace asio } // namespace boost #include #endif // BOOST_ASIO_IMPL_SPAWN_HPP