diff options
Diffstat (limited to '3rdparty/asio/include/asio/impl/spawn.hpp')
-rw-r--r-- | 3rdparty/asio/include/asio/impl/spawn.hpp | 1499 |
1 files changed, 1184 insertions, 315 deletions
diff --git a/3rdparty/asio/include/asio/impl/spawn.hpp b/3rdparty/asio/include/asio/impl/spawn.hpp index 82a05f086a2..38a96406ed4 100644 --- a/3rdparty/asio/include/asio/impl/spawn.hpp +++ b/3rdparty/asio/include/asio/impl/spawn.hpp @@ -2,7 +2,7 @@ // impl/spawn.hpp // ~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// 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) @@ -16,513 +16,1382 @@ #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" +#include <tuple> #include "asio/associated_allocator.hpp" +#include "asio/associated_cancellation_slot.hpp" #include "asio/associated_executor.hpp" #include "asio/async_result.hpp" #include "asio/bind_executor.hpp" #include "asio/detail/atomic_count.hpp" -#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/bind_handler.hpp" #include "asio/detail/handler_cont_helpers.hpp" -#include "asio/detail/handler_invoke_helpers.hpp" #include "asio/detail/memory.hpp" #include "asio/detail/noncopyable.hpp" #include "asio/detail/type_traits.hpp" +#include "asio/detail/utility.hpp" #include "asio/system_error.hpp" +#if defined(ASIO_HAS_BOOST_CONTEXT_FIBER) +# include <boost/context/fiber.hpp> +#endif // defined(ASIO_HAS_BOOST_CONTEXT_FIBER) + #include "asio/detail/push_options.hpp" namespace asio { namespace detail { - template <typename Handler, typename T> - class coro_handler +#if !defined(ASIO_NO_EXCEPTIONS) +inline void spawned_thread_rethrow(void* ex) +{ + if (*static_cast<exception_ptr*>(ex)) + rethrow_exception(*static_cast<exception_ptr*>(ex)); +} +#endif // !defined(ASIO_NO_EXCEPTIONS) + +#if defined(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<void> callee_type; + typedef boost::coroutines::push_coroutine<void> caller_type; +#else + typedef boost::coroutines::coroutine<void()> callee_type; + typedef boost::coroutines::coroutine<void()> caller_type; +#endif + + spawned_coroutine_thread(caller_type& caller) + : caller_(caller), + on_suspend_fn_(0), + on_suspend_arg_(0) { - public: - coro_handler(basic_yield_context<Handler> ctx) - : coro_(ctx.coro_.lock()), - ca_(ctx.ca_), - handler_(ctx.handler_), - ready_(0), - ec_(ctx.ec_), - value_(0) + } + + template <typename F> + 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<decay_t<F>>( + static_cast<F&&>(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 <typename F> + 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&&>(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(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(); + } - void operator()(T value) +private: + template <typename Function> + class entry_point + { + public: + template <typename F> + entry_point(F&& f, + spawned_coroutine_thread** spawned_thread_out) + : function_(static_cast<F&&>(f)), + spawned_thread_out_(spawned_thread_out) { - *ec_ = asio::error_code(); - *value_ = ASIO_MOVE_CAST(T)(value); - if (--*ready_ == 0) - (*coro_)(); } - void operator()(asio::error_code ec, T value) + void operator()(caller_type& caller) { - *ec_ = ec; - *value_ = ASIO_MOVE_CAST(T)(value); - if (--*ready_ == 0) - (*coro_)(); + Function function(static_cast<Function&&>(function_)); + spawned_coroutine_thread spawned_thread(caller); + *spawned_thread_out_ = &spawned_thread; + spawned_thread_out_ = 0; + spawned_thread.suspend(); +#if !defined(ASIO_NO_EXCEPTIONS) + try +#endif // !defined(ASIO_NO_EXCEPTIONS) + { + function(&spawned_thread); + spawned_thread.terminal_ = true; + spawned_thread.suspend(); + } +#if !defined(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(ASIO_NO_EXCEPTIONS) } - //private: - shared_ptr<typename basic_yield_context<Handler>::callee_type> coro_; - typename basic_yield_context<Handler>::caller_type& ca_; - Handler handler_; - atomic_count* ready_; - asio::error_code* ec_; - T* value_; + private: + Function function_; + spawned_coroutine_thread** spawned_thread_out_; }; - template <typename Handler> - class coro_handler<Handler, void> + caller_type& caller_; + callee_type callee_; + void (*on_suspend_fn_)(void*); + void* on_suspend_arg_; +}; + +#endif // defined(ASIO_HAS_BOOST_COROUTINE) + +#if defined(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<fiber_type&&>(caller)), + on_suspend_fn_(0), + on_suspend_arg_(0) { - public: - coro_handler(basic_yield_context<Handler> ctx) - : coro_(ctx.coro_.lock()), - ca_(ctx.ca_), - handler_(ctx.handler_), - ready_(0), - ec_(ctx.ec_) + } + + template <typename StackAllocator, typename F> + 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<StackAllocator&&>(stack_allocator), + entry_point<decay_t<F>>( + static_cast<F&&>(f), &spawned_thread)); + callee = fiber_type(static_cast<fiber_type&&>(callee)).resume(); + spawned_thread->callee_ = static_cast<fiber_type&&>(callee); + spawned_thread->parent_cancellation_slot_ = parent_cancel_slot; + spawned_thread->cancellation_state_ = cancel_state; + return spawned_thread; + } + + template <typename F> + 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&&>(f), parent_cancel_slot, cancel_state); + } + + void resume() + { + callee_ = fiber_type(static_cast<fiber_type&&>(callee_)).resume(); + if (on_suspend_fn_) { + void (*fn)(void*) = on_suspend_fn_; + void* arg = on_suspend_arg_; + on_suspend_fn_ = 0; + fn(arg); } + } - void operator()() + void suspend_with(void (*fn)(void*), void* arg) + { + if (throw_if_cancelled_) + if (!!cancellation_state_.cancelled()) + throw_error(asio::error::operation_aborted, "yield"); + has_context_switched_ = true; + on_suspend_fn_ = fn; + on_suspend_arg_ = arg; + caller_ = fiber_type(static_cast<fiber_type&&>(caller_)).resume(); + } + + void destroy() + { + fiber_type callee = static_cast<fiber_type&&>(callee_); + if (terminal_) + fiber_type(static_cast<fiber_type&&>(callee)).resume(); + } + +private: + template <typename Function> + class entry_point + { + public: + template <typename F> + entry_point(F&& f, + spawned_fiber_thread** spawned_thread_out) + : function_(static_cast<F&&>(f)), + spawned_thread_out_(spawned_thread_out) { - *ec_ = asio::error_code(); - if (--*ready_ == 0) - (*coro_)(); } - void operator()(asio::error_code ec) + fiber_type operator()(fiber_type&& caller) { - *ec_ = ec; - if (--*ready_ == 0) - (*coro_)(); + Function function(static_cast<Function&&>(function_)); + spawned_fiber_thread spawned_thread( + static_cast<fiber_type&&>(caller)); + *spawned_thread_out_ = &spawned_thread; + spawned_thread_out_ = 0; + spawned_thread.suspend(); +#if !defined(ASIO_NO_EXCEPTIONS) + try +#endif // !defined(ASIO_NO_EXCEPTIONS) + { + function(&spawned_thread); + spawned_thread.terminal_ = true; + spawned_thread.suspend(); + } +#if !defined(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(ASIO_NO_EXCEPTIONS) + return static_cast<fiber_type&&>(spawned_thread.caller_); } - //private: - shared_ptr<typename basic_yield_context<Handler>::callee_type> coro_; - typename basic_yield_context<Handler>::caller_type& ca_; - Handler handler_; - atomic_count* ready_; - asio::error_code* ec_; + private: + Function function_; + spawned_fiber_thread** spawned_thread_out_; }; - template <typename Handler, typename T> - inline void* asio_handler_allocate(std::size_t size, - coro_handler<Handler, T>* this_handler) + fiber_type caller_; + fiber_type callee_; + void (*on_suspend_fn_)(void*); + void* on_suspend_arg_; +}; + +#endif // defined(ASIO_HAS_BOOST_CONTEXT_FIBER) + +#if defined(ASIO_HAS_BOOST_CONTEXT_FIBER) +typedef spawned_fiber_thread default_spawned_thread_type; +#elif defined(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_) { - return asio_handler_alloc_helpers::allocate( - size, this_handler->handler_); + other.spawned_thread_ = 0; } - template <typename Handler, typename T> - inline void asio_handler_deallocate(void* pointer, std::size_t size, - coro_handler<Handler, T>* this_handler) + ~spawned_thread_resumer() { - asio_handler_alloc_helpers::deallocate( - pointer, size, this_handler->handler_); + if (spawned_thread_) + spawned_thread_->destroy(); } - template <typename Handler, typename T> - inline bool asio_handler_is_continuation(coro_handler<Handler, T>*) + void operator()() { - return true; + spawned_thread_->attach(&spawned_thread_); + spawned_thread_->resume(); } - template <typename Function, typename Handler, typename T> - inline void asio_handler_invoke(Function& function, - coro_handler<Handler, T>* this_handler) +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) { - asio_handler_invoke_helpers::invoke( - function, this_handler->handler_); + spawned_thread->detach(); } - template <typename Function, typename Handler, typename T> - inline void asio_handler_invoke(const Function& function, - coro_handler<Handler, T>* this_handler) + spawned_thread_destroyer(spawned_thread_destroyer&& other) noexcept + : spawned_thread_(other.spawned_thread_) { - asio_handler_invoke_helpers::invoke( - function, this_handler->handler_); + other.spawned_thread_ = 0; } - template <typename Handler, typename T> - class coro_async_result + ~spawned_thread_destroyer() { - public: - typedef coro_handler<Handler, T> completion_handler_type; - typedef T return_type; + if (spawned_thread_) + spawned_thread_->destroy(); + } - explicit coro_async_result(completion_handler_type& h) - : handler_(h), - ca_(h.ca_), - ready_(2) + void operator()() + { + if (spawned_thread_) { - h.ready_ = &ready_; - out_ec_ = h.ec_; - if (!out_ec_) h.ec_ = &ec_; - h.value_ = &value_; + spawned_thread_->destroy(); + spawned_thread_ = 0; } + } - return_type get() - { - // Must not hold shared_ptr to coro while suspended. - handler_.coro_.reset(); +private: + spawned_thread_base* spawned_thread_; +}; - if (--ready_ != 0) - ca_(); - if (!out_ec_ && ec_) throw asio::system_error(ec_); - return ASIO_MOVE_CAST(return_type)(value_); - } +// Base class for all completion handlers associated with a spawned thread. +template <typename Executor> +class spawn_handler_base +{ +public: + typedef Executor executor_type; + typedef cancellation_slot cancellation_slot_type; - private: - completion_handler_type& handler_; - typename basic_yield_context<Handler>::caller_type& ca_; - atomic_count ready_; - asio::error_code* out_ec_; - asio::error_code ec_; - return_type value_; - }; + spawn_handler_base(const basic_yield_context<Executor>& 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_) - template <typename Handler> - class coro_async_result<Handler, void> { - public: - typedef coro_handler<Handler, void> completion_handler_type; - typedef void return_type; + other.spawned_thread_ = 0; + } - explicit coro_async_result(completion_handler_type& h) - : handler_(h), - ca_(h.ca_), - ready_(2) - { - h.ready_ = &ready_; - out_ec_ = h.ec_; - if (!out_ec_) h.ec_ = &ec_; - } + ~spawn_handler_base() + { + if (spawned_thread_) + (post)(yield_.executor_, spawned_thread_destroyer(spawned_thread_)); + } - void get() - { - // Must not hold shared_ptr to coro while suspended. - handler_.coro_.reset(); + executor_type get_executor() const noexcept + { + return yield_.executor_; + } - if (--ready_ != 0) - ca_(); - if (!out_ec_ && ec_) throw asio::system_error(ec_); - } + cancellation_slot_type get_cancellation_slot() const noexcept + { + return spawned_thread_->get_cancellation_slot(); + } - private: - completion_handler_type& handler_; - typename basic_yield_context<Handler>::caller_type& ca_; - atomic_count ready_; - asio::error_code* out_ec_; - asio::error_code ec_; - }; + void resume() + { + spawned_thread_resumer resumer(spawned_thread_); + spawned_thread_ = 0; + resumer(); + } -} // namespace detail +protected: + const basic_yield_context<Executor>& yield_; + spawned_thread_base* spawned_thread_; +}; -#if !defined(GENERATING_DOCUMENTATION) +// Completion handlers for when basic_yield_context is used as a token. +template <typename Executor, typename Signature> +class spawn_handler; -template <typename Handler, typename ReturnType> -class async_result<basic_yield_context<Handler>, ReturnType()> - : public detail::coro_async_result<Handler, void> +template <typename Executor, typename R> +class spawn_handler<Executor, R()> + : public spawn_handler_base<Executor> { public: - explicit async_result( - typename detail::coro_async_result<Handler, - void>::completion_handler_type& h) - : detail::coro_async_result<Handler, void>(h) + typedef void return_type; + + struct result_type {}; + + spawn_handler(const basic_yield_context<Executor>& yield, result_type&) + : spawn_handler_base<Executor>(yield) + { + } + + void operator()() + { + this->resume(); + } + + static return_type on_resume(result_type&) { } }; -template <typename Handler, typename ReturnType, typename Arg1> -class async_result<basic_yield_context<Handler>, ReturnType(Arg1)> - : public detail::coro_async_result<Handler, typename decay<Arg1>::type> +template <typename Executor, typename R> +class spawn_handler<Executor, R(asio::error_code)> + : public spawn_handler_base<Executor> { public: - explicit async_result( - typename detail::coro_async_result<Handler, - typename decay<Arg1>::type>::completion_handler_type& h) - : detail::coro_async_result<Handler, Arg1>(h) + typedef void return_type; + typedef asio::error_code* result_type; + + spawn_handler(const basic_yield_context<Executor>& yield, result_type& result) + : spawn_handler_base<Executor>(yield), + result_(result) + { + } + + void operator()(asio::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 <typename Handler, typename ReturnType> -class async_result<basic_yield_context<Handler>, - ReturnType(asio::error_code)> - : public detail::coro_async_result<Handler, void> +template <typename Executor, typename R> +class spawn_handler<Executor, R(exception_ptr)> + : public spawn_handler_base<Executor> { public: - explicit async_result( - typename detail::coro_async_result<Handler, - void>::completion_handler_type& h) - : detail::coro_async_result<Handler, void>(h) + typedef void return_type; + typedef exception_ptr* result_type; + + spawn_handler(const basic_yield_context<Executor>& yield, result_type& result) + : spawn_handler_base<Executor>(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 <typename Handler, typename ReturnType, typename Arg2> -class async_result<basic_yield_context<Handler>, - ReturnType(asio::error_code, Arg2)> - : public detail::coro_async_result<Handler, typename decay<Arg2>::type> +template <typename Executor, typename R, typename T> +class spawn_handler<Executor, R(T)> + : public spawn_handler_base<Executor> { public: - explicit async_result( - typename detail::coro_async_result<Handler, - typename decay<Arg2>::type>::completion_handler_type& h) - : detail::coro_async_result<Handler, Arg2>(h) + typedef T return_type; + typedef return_type* result_type; + + spawn_handler(const basic_yield_context<Executor>& yield, result_type& result) + : spawn_handler_base<Executor>(yield), + result_(result) { } -}; -#if !defined(ASIO_NO_DEPRECATED) + void operator()(T value) + { + result_ = &value; + this->resume(); + } -template <typename Handler, typename ReturnType> -struct handler_type<basic_yield_context<Handler>, ReturnType()> -{ - typedef detail::coro_handler<Handler, void> type; + static return_type on_resume(result_type& result) + { + return static_cast<return_type&&>(*result); + } + +private: + result_type& result_; }; -template <typename Handler, typename ReturnType, typename Arg1> -struct handler_type<basic_yield_context<Handler>, ReturnType(Arg1)> +template <typename Executor, typename R, typename T> +class spawn_handler<Executor, R(asio::error_code, T)> + : public spawn_handler_base<Executor> { - typedef detail::coro_handler<Handler, typename decay<Arg1>::type> type; +public: + typedef T return_type; + + struct result_type + { + asio::error_code* ec_; + return_type* value_; + }; + + spawn_handler(const basic_yield_context<Executor>& yield, result_type& result) + : spawn_handler_base<Executor>(yield), + result_(result) + { + } + + void operator()(asio::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<return_type&&>(*result.value_); + } + +private: + result_type& result_; }; -template <typename Handler, typename ReturnType> -struct handler_type<basic_yield_context<Handler>, - ReturnType(asio::error_code)> +template <typename Executor, typename R, typename T> +class spawn_handler<Executor, R(exception_ptr, T)> + : public spawn_handler_base<Executor> { - typedef detail::coro_handler<Handler, void> type; +public: + typedef T return_type; + + struct result_type + { + exception_ptr* ex_; + return_type* value_; + }; + + spawn_handler(const basic_yield_context<Executor>& yield, result_type& result) + : spawn_handler_base<Executor>(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<return_type&&>(*result.value_); + } + +private: + result_type& result_; }; -template <typename Handler, typename ReturnType, typename Arg2> -struct handler_type<basic_yield_context<Handler>, - ReturnType(asio::error_code, Arg2)> +template <typename Executor, typename R, typename... Ts> +class spawn_handler<Executor, R(Ts...)> + : public spawn_handler_base<Executor> { - typedef detail::coro_handler<Handler, typename decay<Arg2>::type> type; +public: + typedef std::tuple<Ts...> return_type; + + typedef return_type* result_type; + + spawn_handler(const basic_yield_context<Executor>& yield, result_type& result) + : spawn_handler_base<Executor>(yield), + result_(result) + { + } + + template <typename... Args> + void operator()(Args&&... args) + { + return_type value(static_cast<Args&&>(args)...); + result_ = &value; + this->resume(); + } + + static return_type on_resume(result_type& result) + { + return static_cast<return_type&&>(*result); + } + +private: + result_type& result_; }; -template <typename Handler, typename T> -class async_result<detail::coro_handler<Handler, T> > - : public detail::coro_async_result<Handler, T> +template <typename Executor, typename R, typename... Ts> +class spawn_handler<Executor, R(asio::error_code, Ts...)> + : public spawn_handler_base<Executor> { public: - typedef typename detail::coro_async_result<Handler, T>::return_type type; + typedef std::tuple<Ts...> return_type; - explicit async_result( - typename detail::coro_async_result<Handler, - void>::completion_handler_type& h) - : detail::coro_async_result<Handler, T>(h) + struct result_type + { + asio::error_code* ec_; + return_type* value_; + }; + + spawn_handler(const basic_yield_context<Executor>& yield, result_type& result) + : spawn_handler_base<Executor>(yield), + result_(result) { } -}; -#endif // !defined(ASIO_NO_DEPRECATED) + template <typename... Args> + void operator()(asio::error_code ec, + Args&&... args) + { + return_type value(static_cast<Args&&>(args)...); + if (this->yield_.ec_) + { + *this->yield_.ec_ = ec; + result_.ec_ = 0; + } + else + result_.ec_ = &ec; + result_.value_ = &value; + this->resume(); + } -template <typename Handler, typename T, typename Allocator> -struct associated_allocator<detail::coro_handler<Handler, T>, Allocator> + static return_type on_resume(result_type& result) + { + if (result.ec_) + throw_error(*result.ec_); + return static_cast<return_type&&>(*result.value_); + } + +private: + result_type& result_; +}; + +template <typename Executor, typename R, typename... Ts> +class spawn_handler<Executor, R(exception_ptr, Ts...)> + : public spawn_handler_base<Executor> { - typedef typename associated_allocator<Handler, Allocator>::type type; +public: + typedef std::tuple<Ts...> return_type; + + struct result_type + { + exception_ptr* ex_; + return_type* value_; + }; + + spawn_handler(const basic_yield_context<Executor>& yield, result_type& result) + : spawn_handler_base<Executor>(yield), + result_(result) + { + } - static type get(const detail::coro_handler<Handler, T>& h, - const Allocator& a = Allocator()) ASIO_NOEXCEPT + template <typename... Args> + void operator()(exception_ptr ex, Args&&... args) { - return associated_allocator<Handler, Allocator>::get(h.handler_, a); + return_type value(static_cast<Args&&>(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<return_type&&>(*result.value_); + } + +private: + result_type& result_; }; -template <typename Handler, typename T, typename Executor> -struct associated_executor<detail::coro_handler<Handler, T>, Executor> +template <typename Executor, typename Signature> +inline bool asio_handler_is_continuation(spawn_handler<Executor, Signature>*) +{ + return true; +} + +} // namespace detail + +template <typename Executor, typename Signature> +class async_result<basic_yield_context<Executor>, Signature> { - typedef typename associated_executor<Handler, Executor>::type type; +public: + typedef typename detail::spawn_handler<Executor, Signature> handler_type; + typedef typename handler_type::return_type return_type; + +#if defined(ASIO_HAS_VARIADIC_LAMBDA_CAPTURES) - static type get(const detail::coro_handler<Handler, T>& h, - const Executor& ex = Executor()) ASIO_NOEXCEPT + template <typename Initiation, typename... InitArgs> + static return_type initiate(Initiation&& init, + const basic_yield_context<Executor>& yield, + InitArgs&&... init_args) { - return associated_executor<Handler, Executor>::get(h.handler_, ex); + typename handler_type::result_type result + = typename handler_type::result_type(); + + yield.spawned_thread_->suspend_with( + [&]() + { + static_cast<Initiation&&>(init)( + handler_type(yield, result), + static_cast<InitArgs&&>(init_args)...); + }); + + return handler_type::on_resume(result); } -}; -namespace detail { +#else // defined(ASIO_HAS_VARIADIC_LAMBDA_CAPTURES) - template <typename Handler, typename Function> - struct spawn_data : private noncopyable + template <typename Initiation, typename... InitArgs> + struct suspend_with_helper { - spawn_data(ASIO_MOVE_ARG(Handler) handler, - bool call_handler, ASIO_MOVE_ARG(Function) function) - : handler_(ASIO_MOVE_CAST(Handler)(handler)), - call_handler_(call_handler), - function_(ASIO_MOVE_CAST(Function)(function)) + typename handler_type::result_type& result_; + Initiation&& init_; + const basic_yield_context<Executor>& yield_; + std::tuple<InitArgs&&...> init_args_; + + template <std::size_t... I> + void do_invoke(detail::index_sequence<I...>) { + static_cast<Initiation&&>(init_)( + handler_type(yield_, result_), + static_cast<InitArgs&&>(std::get<I>(init_args_))...); } - weak_ptr<typename basic_yield_context<Handler>::callee_type> coro_; - Handler handler_; - bool call_handler_; - Function function_; + void operator()() + { + this->do_invoke(detail::make_index_sequence<sizeof...(InitArgs)>()); + } }; - template <typename Handler, typename Function> - struct coro_entry_point + template <typename Initiation, typename... InitArgs> + static return_type initiate(Initiation&& init, + const basic_yield_context<Executor>& yield, + InitArgs&&... init_args) + { + typename handler_type::result_type result + = typename handler_type::result_type(); + + yield.spawned_thread_->suspend_with( + suspend_with_helper<Initiation, InitArgs...>{ + result, static_cast<Initiation&&>(init), yield, + std::tuple<InitArgs&&...>( + static_cast<InitArgs&&>(init_args)...)}); + + return handler_type::on_resume(result); + } + +#endif // defined(ASIO_HAS_VARIADIC_LAMBDA_CAPTURES) +}; + +namespace detail { + +template <typename Executor, typename Function, typename Handler> +class spawn_entry_point +{ +public: + template <typename F, typename H> + spawn_entry_point(const Executor& ex, + F&& f, H&& h) + : executor_(ex), + function_(static_cast<F&&>(f)), + handler_(static_cast<H&&>(h)), + work_(handler_, executor_) + { + } + + void operator()(spawned_thread_base* spawned_thread) { - void operator()(typename basic_yield_context<Handler>::caller_type& ca) + const basic_yield_context<Executor> yield(spawned_thread, executor_); + this->call(yield, + void_type<result_of_t<Function(basic_yield_context<Executor>)>>()); + } + +private: + void call(const basic_yield_context<Executor>& yield, void_type<void>) + { +#if !defined(ASIO_NO_EXCEPTIONS) + try +#endif // !defined(ASIO_NO_EXCEPTIONS) { - shared_ptr<spawn_data<Handler, Function> > data(data_); -#if !defined(BOOST_COROUTINES_UNIDIRECT) && !defined(BOOST_COROUTINES_V2) - ca(); // Yield until coroutine pointer has been initialised. -#endif // !defined(BOOST_COROUTINES_UNIDIRECT) && !defined(BOOST_COROUTINES_V2) - const basic_yield_context<Handler> yield( - data->coro_, ca, data->handler_); - - (data->function_)(yield); - if (data->call_handler_) - (data->handler_)(); + function_(yield); + if (!yield.spawned_thread_->has_context_switched()) + (post)(yield); + detail::binder1<Handler, exception_ptr> + handler(handler_, exception_ptr()); + work_.complete(handler, handler.handler_); } +#if !defined(ASIO_NO_EXCEPTIONS) +# if defined(ASIO_HAS_BOOST_CONTEXT_FIBER) + catch (const boost::context::detail::forced_unwind&) + { + throw; + } +# endif // defined(ASIO_HAS_BOOST_CONTEXT_FIBER) +# if defined(ASIO_HAS_BOOST_COROUTINE) + catch (const boost::coroutines::detail::forced_unwind&) + { + throw; + } +# endif // defined(ASIO_HAS_BOOST_COROUTINE) + catch (...) + { + exception_ptr ex = current_exception(); + if (!yield.spawned_thread_->has_context_switched()) + (post)(yield); + detail::binder1<Handler, exception_ptr> handler(handler_, ex); + work_.complete(handler, handler.handler_); + } +#endif // !defined(ASIO_NO_EXCEPTIONS) + } - shared_ptr<spawn_data<Handler, Function> > data_; - }; - - template <typename Handler, typename Function> - struct spawn_helper + template <typename T> + void call(const basic_yield_context<Executor>& yield, void_type<T>) { - void operator()() +#if !defined(ASIO_NO_EXCEPTIONS) + try +#endif // !defined(ASIO_NO_EXCEPTIONS) + { + T result(function_(yield)); + if (!yield.spawned_thread_->has_context_switched()) + (post)(yield); + detail::binder2<Handler, exception_ptr, T> + handler(handler_, exception_ptr(), static_cast<T&&>(result)); + work_.complete(handler, handler.handler_); + } +#if !defined(ASIO_NO_EXCEPTIONS) +# if defined(ASIO_HAS_BOOST_CONTEXT_FIBER) + catch (const boost::context::detail::forced_unwind&) + { + throw; + } +# endif // defined(ASIO_HAS_BOOST_CONTEXT_FIBER) +# if defined(ASIO_HAS_BOOST_COROUTINE) + catch (const boost::coroutines::detail::forced_unwind&) + { + throw; + } +# endif // defined(ASIO_HAS_BOOST_COROUTINE) + catch (...) { - typedef typename basic_yield_context<Handler>::callee_type callee_type; - coro_entry_point<Handler, Function> entry_point = { data_ }; - shared_ptr<callee_type> coro(new callee_type(entry_point, attributes_)); - data_->coro_ = coro; - (*coro)(); + exception_ptr ex = current_exception(); + if (!yield.spawned_thread_->has_context_switched()) + (post)(yield); + detail::binder2<Handler, exception_ptr, T> handler(handler_, ex, T()); + work_.complete(handler, handler.handler_); } +#endif // !defined(ASIO_NO_EXCEPTIONS) + } - shared_ptr<spawn_data<Handler, Function> > data_; - boost::coroutines::attributes attributes_; - }; + Executor executor_; + Function function_; + Handler handler_; + handler_work<Handler, Executor> work_; +}; + +struct spawn_cancellation_signal_emitter +{ + cancellation_signal* signal_; + cancellation_type_t type_; - template <typename Function, typename Handler, typename Function1> - inline void asio_handler_invoke(Function& function, - spawn_helper<Handler, Function1>* this_handler) + void operator()() { - asio_handler_invoke_helpers::invoke( - function, this_handler->data_->handler_); + signal_->emit(type_); } +}; - template <typename Function, typename Handler, typename Function1> - inline void asio_handler_invoke(const Function& function, - spawn_helper<Handler, Function1>* this_handler) +template <typename Handler, typename Executor, typename = void> +class spawn_cancellation_handler +{ +public: + spawn_cancellation_handler(const Handler&, const Executor& ex) + : ex_(ex) { - asio_handler_invoke_helpers::invoke( - function, this_handler->data_->handler_); } - inline void default_spawn_handler() {} + cancellation_slot slot() + { + return signal_.slot(); + } -} // namespace detail + void operator()(cancellation_type_t type) + { + spawn_cancellation_signal_emitter emitter = { &signal_, type }; + (dispatch)(ex_, emitter); + } -template <typename Function> -inline void spawn(ASIO_MOVE_ARG(Function) function, - const boost::coroutines::attributes& attributes) +private: + cancellation_signal signal_; + Executor ex_; +}; + + +template <typename Handler, typename Executor> +class spawn_cancellation_handler<Handler, Executor, + enable_if_t< + is_same< + typename associated_executor<Handler, + Executor>::asio_associated_executor_is_unspecialised, + void + >::value + >> { - typedef typename decay<Function>::type function_type; +public: + spawn_cancellation_handler(const Handler&, const Executor&) + { + } - typename associated_executor<function_type>::type ex( - (get_associated_executor)(function)); + cancellation_slot slot() + { + return signal_.slot(); + } + + void operator()(cancellation_type_t type) + { + signal_.emit(type); + } + +private: + cancellation_signal signal_; +}; + +template <typename Executor> +class initiate_spawn +{ +public: + typedef Executor executor_type; - asio::spawn(ex, ASIO_MOVE_CAST(Function)(function), attributes); + explicit initiate_spawn(const executor_type& ex) + : executor_(ex) + { + } + + executor_type get_executor() const noexcept + { + return executor_; + } + + template <typename Handler, typename F> + void operator()(Handler&& handler, + F&& f) const + { + typedef decay_t<Handler> handler_type; + typedef decay_t<F> function_type; + typedef spawn_cancellation_handler< + handler_type, Executor> cancel_handler_type; + + associated_cancellation_slot_t<handler_type> slot + = asio::get_associated_cancellation_slot(handler); + + cancel_handler_type* cancel_handler = slot.is_connected() + ? &slot.template emplace<cancel_handler_type>(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, function_type, handler_type>( + executor_, static_cast<F&&>(f), + static_cast<Handler&&>(handler)), + proxy_slot, cancel_state))); + } + +#if defined(ASIO_HAS_BOOST_CONTEXT_FIBER) + + template <typename Handler, typename StackAllocator, typename F> + void operator()(Handler&& handler, allocator_arg_t, + StackAllocator&& stack_allocator, + F&& f) const + { + typedef decay_t<Handler> handler_type; + typedef decay_t<F> function_type; + typedef spawn_cancellation_handler< + handler_type, Executor> cancel_handler_type; + + associated_cancellation_slot_t<handler_type> slot + = asio::get_associated_cancellation_slot(handler); + + cancel_handler_type* cancel_handler = slot.is_connected() + ? &slot.template emplace<cancel_handler_type>(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<StackAllocator&&>(stack_allocator), + spawn_entry_point<Executor, function_type, handler_type>( + executor_, static_cast<F&&>(f), + static_cast<Handler&&>(handler)), + proxy_slot, cancel_state))); + } + +#endif // defined(ASIO_HAS_BOOST_CONTEXT_FIBER) + +private: + executor_type executor_; +}; + +} // namespace detail + +template <typename Executor, typename F, + ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature< + result_of_t<F(basic_yield_context<Executor>)>>::type) CompletionToken> +inline auto spawn(const Executor& ex, F&& function, CompletionToken&& token, +#if defined(ASIO_HAS_BOOST_COROUTINE) + constraint_t< + !is_same< + decay_t<CompletionToken>, + boost::coroutines::attributes + >::value + >, +#endif // defined(ASIO_HAS_BOOST_COROUTINE) + constraint_t< + is_executor<Executor>::value || execution::is_executor<Executor>::value + >) + -> decltype( + async_initiate<CompletionToken, + typename detail::spawn_signature< + result_of_t<F(basic_yield_context<Executor>)>>::type>( + declval<detail::initiate_spawn<Executor>>(), + token, static_cast<F&&>(function))) +{ + return async_initiate<CompletionToken, + typename detail::spawn_signature< + result_of_t<F(basic_yield_context<Executor>)>>::type>( + detail::initiate_spawn<Executor>(ex), + token, static_cast<F&&>(function)); } -template <typename Handler, typename Function> -void spawn(ASIO_MOVE_ARG(Handler) handler, - ASIO_MOVE_ARG(Function) function, - const boost::coroutines::attributes& attributes, - typename enable_if<!is_executor<typename decay<Handler>::type>::value && - !is_convertible<Handler&, execution_context&>::value>::type*) +template <typename ExecutionContext, typename F, + ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature< + result_of_t<F(basic_yield_context< + typename ExecutionContext::executor_type>)>>::type) CompletionToken> +inline auto spawn(ExecutionContext& ctx, F&& function, CompletionToken&& token, +#if defined(ASIO_HAS_BOOST_COROUTINE) + constraint_t< + !is_same< + decay_t<CompletionToken>, + boost::coroutines::attributes + >::value + >, +#endif // defined(ASIO_HAS_BOOST_COROUTINE) + constraint_t< + is_convertible<ExecutionContext&, execution_context&>::value + >) + -> decltype( + async_initiate<CompletionToken, + typename detail::spawn_signature< + result_of_t<F(basic_yield_context< + typename ExecutionContext::executor_type>)>>::type>( + declval<detail::initiate_spawn< + typename ExecutionContext::executor_type>>(), + token, static_cast<F&&>(function))) { - typedef typename decay<Handler>::type handler_type; + return (spawn)(ctx.get_executor(), static_cast<F&&>(function), + static_cast<CompletionToken&&>(token)); +} - typename associated_executor<handler_type>::type ex( - (get_associated_executor)(handler)); +template <typename Executor, typename F, + ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature< + result_of_t<F(basic_yield_context<Executor>)>>::type) + CompletionToken> +inline auto spawn(const basic_yield_context<Executor>& ctx, + F&& function, CompletionToken&& token, +#if defined(ASIO_HAS_BOOST_COROUTINE) + constraint_t< + !is_same< + decay_t<CompletionToken>, + boost::coroutines::attributes + >::value + >, +#endif // defined(ASIO_HAS_BOOST_COROUTINE) + constraint_t< + is_executor<Executor>::value || execution::is_executor<Executor>::value + >) + -> decltype( + async_initiate<CompletionToken, + typename detail::spawn_signature< + result_of_t<F(basic_yield_context<Executor>)>>::type>( + declval<detail::initiate_spawn<Executor>>(), + token, static_cast<F&&>(function))) +{ + return (spawn)(ctx.get_executor(), static_cast<F&&>(function), + static_cast<CompletionToken&&>(token)); +} - typename associated_allocator<handler_type>::type a( - (get_associated_allocator)(handler)); +#if defined(ASIO_HAS_BOOST_CONTEXT_FIBER) + +template <typename Executor, typename StackAllocator, typename F, + ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature< + result_of_t<F(basic_yield_context<Executor>)>>::type) + CompletionToken> +inline auto spawn(const Executor& ex, allocator_arg_t, + StackAllocator&& stack_allocator, F&& function, CompletionToken&& token, + constraint_t< + is_executor<Executor>::value || execution::is_executor<Executor>::value + >) + -> decltype( + async_initiate<CompletionToken, + typename detail::spawn_signature< + result_of_t<F(basic_yield_context<Executor>)>>::type>( + declval<detail::initiate_spawn<Executor>>(), + token, allocator_arg_t(), + static_cast<StackAllocator&&>(stack_allocator), + static_cast<F&&>(function))) +{ + return async_initiate<CompletionToken, + typename detail::spawn_signature< + result_of_t<F(basic_yield_context<Executor>)>>::type>( + detail::initiate_spawn<Executor>(ex), token, allocator_arg_t(), + static_cast<StackAllocator&&>(stack_allocator), + static_cast<F&&>(function)); +} - detail::spawn_helper<handler_type, Function> helper; - helper.data_.reset( - new detail::spawn_data<handler_type, Function>( - ASIO_MOVE_CAST(Handler)(handler), true, - ASIO_MOVE_CAST(Function)(function))); - helper.attributes_ = attributes; +template <typename ExecutionContext, typename StackAllocator, typename F, + ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature< + result_of_t<F(basic_yield_context< + typename ExecutionContext::executor_type>)>>::type) CompletionToken> +inline auto spawn(ExecutionContext& ctx, allocator_arg_t, + StackAllocator&& stack_allocator, F&& function, CompletionToken&& token, + constraint_t< + is_convertible<ExecutionContext&, execution_context&>::value + >) + -> decltype( + async_initiate<CompletionToken, + typename detail::spawn_signature< + result_of_t<F(basic_yield_context< + typename ExecutionContext::executor_type>)>>::type>( + declval<detail::initiate_spawn< + typename ExecutionContext::executor_type>>(), + token, allocator_arg_t(), + static_cast<StackAllocator&&>(stack_allocator), + static_cast<F&&>(function))) +{ + return (spawn)(ctx.get_executor(), allocator_arg_t(), + static_cast<StackAllocator&&>(stack_allocator), + static_cast<F&&>(function), static_cast<CompletionToken&&>(token)); +} - ex.dispatch(helper, a); +template <typename Executor, typename StackAllocator, typename F, + ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature< + result_of_t<F(basic_yield_context<Executor>)>>::type) CompletionToken> +inline auto spawn(const basic_yield_context<Executor>& ctx, allocator_arg_t, + StackAllocator&& stack_allocator, F&& function, CompletionToken&& token, + constraint_t< + is_executor<Executor>::value || execution::is_executor<Executor>::value + >) + -> decltype( + async_initiate<CompletionToken, + typename detail::spawn_signature< + result_of_t<F(basic_yield_context<Executor>)>>::type>( + declval<detail::initiate_spawn<Executor>>(), token, + allocator_arg_t(), static_cast<StackAllocator&&>(stack_allocator), + static_cast<F&&>(function))) +{ + return (spawn)(ctx.get_executor(), allocator_arg_t(), + static_cast<StackAllocator&&>(stack_allocator), + static_cast<F&&>(function), static_cast<CompletionToken&&>(token)); } -template <typename Handler, typename Function> -void spawn(basic_yield_context<Handler> ctx, - ASIO_MOVE_ARG(Function) function, - const boost::coroutines::attributes& attributes) +#endif // defined(ASIO_HAS_BOOST_CONTEXT_FIBER) + +#if defined(ASIO_HAS_BOOST_COROUTINE) + +namespace detail { + +template <typename Executor, typename Function, typename Handler> +class old_spawn_entry_point { - Handler handler(ctx.handler_); // Explicit copy that might be moved from. +public: + template <typename F, typename H> + old_spawn_entry_point(const Executor& ex, F&& f, H&& h) + : executor_(ex), + function_(static_cast<F&&>(f)), + handler_(static_cast<H&&>(h)) + { + } - typename associated_executor<Handler>::type ex( - (get_associated_executor)(handler)); + void operator()(spawned_thread_base* spawned_thread) + { + const basic_yield_context<Executor> yield(spawned_thread, executor_); + this->call(yield, + void_type<result_of_t<Function(basic_yield_context<Executor>)>>()); + } - typename associated_allocator<Handler>::type a( - (get_associated_allocator)(handler)); +private: + void call(const basic_yield_context<Executor>& yield, void_type<void>) + { + function_(yield); + static_cast<Handler&&>(handler_)(); + } - detail::spawn_helper<Handler, Function> helper; - helper.data_.reset( - new detail::spawn_data<Handler, Function>( - ASIO_MOVE_CAST(Handler)(handler), false, - ASIO_MOVE_CAST(Function)(function))); - helper.attributes_ = attributes; + template <typename T> + void call(const basic_yield_context<Executor>& yield, void_type<T>) + { + static_cast<Handler&&>(handler_)(function_(yield)); + } - ex.dispatch(helper, a); + Executor executor_; + Function function_; + Handler handler_; +}; + +inline void default_spawn_handler() {} + +} // namespace detail + +template <typename Function> +inline void spawn(Function&& function, + const boost::coroutines::attributes& attributes) +{ + associated_executor_t<decay_t<Function>> ex( + (get_associated_executor)(function)); + + asio::spawn(ex, static_cast<Function&&>(function), attributes); +} + +template <typename Handler, typename Function> +void spawn(Handler&& handler, Function&& function, + const boost::coroutines::attributes& attributes, + constraint_t< + !is_executor<decay_t<Handler>>::value && + !execution::is_executor<decay_t<Handler>>::value && + !is_convertible<Handler&, execution_context&>::value>) +{ + typedef associated_executor_t<decay_t<Handler>> 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<executor_type, + decay_t<Function>, void (*)()>( + ex, static_cast<Function&&>(function), + &detail::default_spawn_handler), attributes))); +} + +template <typename Executor, typename Function> +void spawn(basic_yield_context<Executor> 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<Executor, + decay_t<Function>, void (*)()>( + ctx.get_executor(), static_cast<Function&&>(function), + &detail::default_spawn_handler), attributes))); } template <typename Function, typename Executor> -inline void spawn(const Executor& ex, - ASIO_MOVE_ARG(Function) function, +inline void spawn(const Executor& ex, Function&& function, const boost::coroutines::attributes& attributes, - typename enable_if<is_executor<Executor>::value>::type*) + constraint_t< + is_executor<Executor>::value || execution::is_executor<Executor>::value + >) { asio::spawn(asio::strand<Executor>(ex), - ASIO_MOVE_CAST(Function)(function), attributes); + static_cast<Function&&>(function), attributes); } template <typename Function, typename Executor> -inline void spawn(const strand<Executor>& ex, - ASIO_MOVE_ARG(Function) function, +inline void spawn(const strand<Executor>& ex, Function&& function, const boost::coroutines::attributes& attributes) { asio::spawn(asio::bind_executor( ex, &detail::default_spawn_handler), - ASIO_MOVE_CAST(Function)(function), attributes); + static_cast<Function&&>(function), attributes); } +#if !defined(ASIO_NO_TS_EXECUTORS) + template <typename Function> -inline void spawn(const asio::io_context::strand& s, - ASIO_MOVE_ARG(Function) function, +inline void spawn(const asio::io_context::strand& s, Function&& function, const boost::coroutines::attributes& attributes) { asio::spawn(asio::bind_executor( s, &detail::default_spawn_handler), - ASIO_MOVE_CAST(Function)(function), attributes); + static_cast<Function&&>(function), attributes); } +#endif // !defined(ASIO_NO_TS_EXECUTORS) + template <typename Function, typename ExecutionContext> -inline void spawn(ExecutionContext& ctx, - ASIO_MOVE_ARG(Function) function, +inline void spawn(ExecutionContext& ctx, Function&& function, const boost::coroutines::attributes& attributes, - typename enable_if<is_convertible< - ExecutionContext&, execution_context&>::value>::type*) + constraint_t< + is_convertible<ExecutionContext&, execution_context&>::value + >) { asio::spawn(ctx.get_executor(), - ASIO_MOVE_CAST(Function)(function), attributes); + static_cast<Function&&>(function), attributes); } -#endif // !defined(GENERATING_DOCUMENTATION) +#endif // defined(ASIO_HAS_BOOST_COROUTINE) } // namespace asio |