diff --git a/doc/overview/spawn.qbk b/doc/overview/spawn.qbk index b1615b97..0c360694 100644 --- a/doc/overview/spawn.qbk +++ b/doc/overview/spawn.qbk @@ -12,7 +12,7 @@ running stackful coroutines. It is based on the Boost.Coroutine library. The `spawn()` function enables programs to implement asynchronous logic in a synchronous manner, as shown in the following example: - boost::asio::spawn(my_strand, do_echo); + boost::asio::spawn(my_strand, do_echo, boost::asio::detached); // ... @@ -37,10 +37,7 @@ synchronous manner, as shown in the following example: } } -The first argument to `spawn()` may be a -[link boost_asio.reference.io_context__strand `strand`], -[link boost_asio.reference.io_context `io_context`], or a -[link boost_asio.reference.Handler completion handler]. +The first argument to `spawn()` may be an executor or execution context. This argument determines the context in which the coroutine is permitted to execute. For example, a server's per-client object may consist of multiple coroutines; they should all run on the same `strand` so that no explicit @@ -85,10 +82,10 @@ exception, associate the output variable with the `yield_context` as follows: my_socket.async_read_some( boost::asio::buffer(data), yield[ec]); -[*Note:] if `spawn()` is used with a custom completion handler of type -`Handler`, the function object signature is actually: +[*Note:] if `spawn()` is used with a specified executor of type `Executor`, the +function object signature is actually: - void coroutine(boost::asio::basic_yield_context yield); + void coroutine(boost::asio::basic_yield_context yield); [heading See Also] diff --git a/doc/reference.xsl b/doc/reference.xsl index 48f80e41..0c51d393 100644 --- a/doc/reference.xsl +++ b/doc/reference.xsl @@ -1610,6 +1610,9 @@ + + + @@ -1712,6 +1715,9 @@ + + + @@ -1853,6 +1859,9 @@ + + + diff --git a/example/cpp03/spawn/echo_server.cpp b/example/cpp03/spawn/echo_server.cpp index ba634105..913cbecc 100644 --- a/example/cpp03/spawn/echo_server.cpp +++ b/example/cpp03/spawn/echo_server.cpp @@ -8,6 +8,7 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // +#include #include #include #include @@ -39,10 +40,12 @@ public: { boost::asio::spawn(strand_, boost::bind(&session::echo, - shared_from_this(), boost::placeholders::_1)); + shared_from_this(), boost::placeholders::_1), + boost::asio::detached_t()); boost::asio::spawn(strand_, boost::bind(&session::timeout, - shared_from_this(), boost::placeholders::_1)); + shared_from_this(), boost::placeholders::_1), + boost::asio::detached_t()); } private: @@ -108,8 +111,9 @@ int main(int argc, char* argv[]) boost::asio::io_context io_context; boost::asio::spawn(io_context, - boost::bind(do_accept, - boost::ref(io_context), atoi(argv[1]), boost::placeholders::_1)); + boost::bind(do_accept, boost::ref(io_context), + atoi(argv[1]), boost::placeholders::_1), + boost::asio::detached_t()); io_context.run(); } diff --git a/example/cpp03/spawn/parallel_grep.cpp b/example/cpp03/spawn/parallel_grep.cpp index 60ee7885..7c4b764e 100644 --- a/example/cpp03/spawn/parallel_grep.cpp +++ b/example/cpp03/spawn/parallel_grep.cpp @@ -8,6 +8,7 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // +#include #include #include #include @@ -19,6 +20,7 @@ #include #include +using boost::asio::detached_t; using boost::asio::dispatch; using boost::asio::spawn; using boost::asio::strand; @@ -74,8 +76,10 @@ int main(int argc, char* argv[]) for (int argn = 2; argn < argc; ++argn) { std::string input_file = argv[argn]; - spawn(pool, boost::bind(&search_file, search_string, - input_file, output_strand, boost::placeholders::_1)); + spawn(pool, + boost::bind(&search_file, search_string, + input_file, output_strand, boost::placeholders::_1), + detached_t()); } // Join the thread pool to wait for all the spawned tasks to complete. diff --git a/example/cpp11/spawn/echo_server.cpp b/example/cpp11/spawn/echo_server.cpp index 7e1d4404..b2eff11e 100644 --- a/example/cpp11/spawn/echo_server.cpp +++ b/example/cpp11/spawn/echo_server.cpp @@ -8,6 +8,7 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // +#include #include #include #include @@ -49,7 +50,7 @@ public: socket_.close(); timer_.cancel(); } - }); + }, boost::asio::detached); boost::asio::spawn(strand_, [this, self](boost::asio::yield_context yield) @@ -61,7 +62,7 @@ public: if (timer_.expires_from_now() <= std::chrono::seconds(0)) socket_.close(); } - }); + }, boost::asio::detached); } private: @@ -98,7 +99,7 @@ int main(int argc, char* argv[]) std::make_shared(io_context, std::move(socket))->go(); } } - }); + }, boost::asio::detached); io_context.run(); } diff --git a/example/cpp11/spawn/parallel_grep.cpp b/example/cpp11/spawn/parallel_grep.cpp index 819b133e..d2ab2256 100644 --- a/example/cpp11/spawn/parallel_grep.cpp +++ b/example/cpp11/spawn/parallel_grep.cpp @@ -8,6 +8,7 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // +#include #include #include #include @@ -17,6 +18,7 @@ #include #include +using boost::asio::detached; using boost::asio::dispatch; using boost::asio::spawn; using boost::asio::strand; @@ -69,7 +71,7 @@ int main(int argc, char* argv[]) if (++line_num % 10 == 0) post(yield); } - }); + }, detached); } // Join the thread pool to wait for all the spawned tasks to complete. diff --git a/include/boost/asio/detail/config.hpp b/include/boost/asio/detail/config.hpp index 77386332..473b6d3b 100644 --- a/include/boost/asio/detail/config.hpp +++ b/include/boost/asio/detail/config.hpp @@ -844,6 +844,35 @@ # endif // !defined(BOOST_ASIO_DISABLE_BOOST_DATE_TIME) #endif // !defined(BOOST_ASIO_HAS_BOOST_DATE_TIME) +// Boost support for the Coroutine library. +#if !defined(BOOST_ASIO_HAS_BOOST_COROUTINE) +# if !defined(BOOST_ASIO_DISABLE_BOOST_COROUTINE) +# define BOOST_ASIO_HAS_BOOST_COROUTINE 1 +# endif // !defined(BOOST_ASIO_DISABLE_BOOST_COROUTINE) +#endif // !defined(BOOST_ASIO_HAS_BOOST_COROUTINE) + +// Boost support for the Context library's fibers. +#if !defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER) +# if !defined(BOOST_ASIO_DISABLE_BOOST_CONTEXT_FIBER) +# if defined(__clang__) +# if (__cplusplus >= 201103) +# define BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER 1 +# endif // (__cplusplus >= 201103) +# elif defined(__GNUC__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 8)) || (__GNUC__ > 4) +# if (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# define BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER 1 +# endif // (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 8)) || (__GNUC__ > 4) +# endif // defined(__GNUC__) +# if defined(BOOST_ASIO_MSVC) +# if (_MSVC_LANG >= 201103) +# define BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER 1 +# endif // (_MSC_LANG >= 201103) +# endif // defined(BOOST_ASIO_MSVC) +# endif // !defined(BOOST_ASIO_DISABLE_BOOST_CONTEXT_FIBER) +#endif // !defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER) + // Standard library support for addressof. #if !defined(BOOST_ASIO_HAS_STD_ADDRESSOF) # if !defined(BOOST_ASIO_DISABLE_STD_ADDRESSOF) diff --git a/include/boost/asio/detail/exception.hpp b/include/boost/asio/detail/exception.hpp new file mode 100644 index 00000000..dfb45151 --- /dev/null +++ b/include/boost/asio/detail/exception.hpp @@ -0,0 +1,42 @@ +// +// detail/exception.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2022 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_DETAIL_EXCEPTION_HPP +#define BOOST_ASIO_DETAIL_EXCEPTION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#if defined(BOOST_ASIO_HAS_STD_EXCEPTION) +# include +#else // defined(BOOST_ASIO_HAS_STD_EXCEPTION) +# include +#endif // defined(BOOST_ASIO_HAS_STD_EXCEPTION) + +namespace boost { +namespace asio { + +#if defined(BOOST_ASIO_HAS_STD_EXCEPTION) +using std::exception_ptr; +using std::current_exception; +using std::rethrow_exception; +#else // defined(BOOST_ASIO_HAS_STD_EXCEPTION) +using boost::exception_ptr; +using boost::current_exception; +using boost::rethrow_exception; +#endif // defined(BOOST_ASIO_HAS_STD_EXCEPTION) + +} // namespace asio +} // namespace boost + +#endif // BOOST_ASIO_DETAIL_EXCEPTION_HPP diff --git a/include/boost/asio/impl/spawn.hpp b/include/boost/asio/impl/spawn.hpp index 43778cf8..29ed1c61 100644 --- a/include/boost/asio/impl/spawn.hpp +++ b/include/boost/asio/impl/spawn.hpp @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -27,385 +28,1334 @@ #include #include #include +#include #include +#if defined(BOOST_ASIO_HAS_STD_TUPLE) +# include +#endif // defined(BOOST_ASIO_HAS_STD_TUPLE) + +#if defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER) +# include +#endif // defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER) + #include namespace boost { namespace asio { namespace detail { - template - class coro_handler +#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(BOOST_ASIO_MOVE_ARG(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::type>( + BOOST_ASIO_MOVE_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 + static spawned_thread_base* spawn(BOOST_ASIO_MOVE_ARG(F) f, + cancellation_slot parent_cancel_slot = cancellation_slot(), + cancellation_state cancel_state = cancellation_state()) + { + return spawn(BOOST_ASIO_MOVE_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(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_); + } + +private: + template + class entry_point { public: - coro_handler(basic_yield_context ctx) - : coro_(ctx.coro_.lock()), - ca_(ctx.ca_), - handler_(ctx.handler_), - ready_(0), - ec_(ctx.ec_), - value_(0) + template + entry_point(BOOST_ASIO_MOVE_ARG(F) f, + spawned_coroutine_thread** spawned_thread_out) + : function_(BOOST_ASIO_MOVE_CAST(F)(f)), + spawned_thread_out_(spawned_thread_out) { } - void operator()(T value) + void operator()(caller_type& caller) { - *ec_ = boost::system::error_code(); - *value_ = BOOST_ASIO_MOVE_CAST(T)(value); - if (--*ready_ == 0) - (*coro_)(); - } - - void operator()(boost::system::error_code ec, T value) - { - *ec_ = ec; - *value_ = BOOST_ASIO_MOVE_CAST(T)(value); - if (--*ready_ == 0) - (*coro_)(); - } - - //private: - shared_ptr::callee_type> coro_; - typename basic_yield_context::caller_type& ca_; - Handler handler_; - atomic_count* ready_; - boost::system::error_code* ec_; - T* value_; - }; - - template - class coro_handler - { - public: - coro_handler(basic_yield_context ctx) - : coro_(ctx.coro_.lock()), - ca_(ctx.ca_), - handler_(ctx.handler_), - ready_(0), - ec_(ctx.ec_) - { - } - - void operator()() - { - *ec_ = boost::system::error_code(); - if (--*ready_ == 0) - (*coro_)(); - } - - void operator()(boost::system::error_code ec) - { - *ec_ = ec; - if (--*ready_ == 0) - (*coro_)(); - } - - //private: - shared_ptr::callee_type> coro_; - typename basic_yield_context::caller_type& ca_; - Handler handler_; - atomic_count* ready_; - boost::system::error_code* ec_; - }; - - template - inline asio_handler_allocate_is_deprecated - asio_handler_allocate(std::size_t size, - coro_handler* this_handler) - { -#if defined(BOOST_ASIO_NO_DEPRECATED) - boost_asio_handler_alloc_helpers::allocate(size, this_handler->handler_); - return asio_handler_allocate_is_no_longer_used(); -#else // defined(BOOST_ASIO_NO_DEPRECATED) - return boost_asio_handler_alloc_helpers::allocate( - size, this_handler->handler_); -#endif // defined(BOOST_ASIO_NO_DEPRECATED) - } - - template - inline asio_handler_deallocate_is_deprecated - asio_handler_deallocate(void* pointer, std::size_t size, - coro_handler* this_handler) - { - boost_asio_handler_alloc_helpers::deallocate( - pointer, size, this_handler->handler_); -#if defined(BOOST_ASIO_NO_DEPRECATED) - return asio_handler_deallocate_is_no_longer_used(); -#endif // defined(BOOST_ASIO_NO_DEPRECATED) - } - - template - inline bool asio_handler_is_continuation(coro_handler*) - { - return true; - } - - template - inline asio_handler_invoke_is_deprecated - asio_handler_invoke(Function& function, - coro_handler* this_handler) - { - boost_asio_handler_invoke_helpers::invoke( - function, this_handler->handler_); -#if defined(BOOST_ASIO_NO_DEPRECATED) - return asio_handler_invoke_is_no_longer_used(); -#endif // defined(BOOST_ASIO_NO_DEPRECATED) - } - - template - inline asio_handler_invoke_is_deprecated - asio_handler_invoke(const Function& function, - coro_handler* this_handler) - { - boost_asio_handler_invoke_helpers::invoke( - function, this_handler->handler_); -#if defined(BOOST_ASIO_NO_DEPRECATED) - return asio_handler_invoke_is_no_longer_used(); -#endif // defined(BOOST_ASIO_NO_DEPRECATED) - } - - template - class coro_async_result - { - public: - typedef coro_handler completion_handler_type; - typedef T return_type; - - 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_; - h.value_ = &value_; - } - - return_type get() - { - // Must not hold shared_ptr to coro while suspended. - handler_.coro_.reset(); - - if (--ready_ != 0) - ca_(); - if (!out_ec_ && ec_) throw boost::system::system_error(ec_); - return BOOST_ASIO_MOVE_CAST(return_type)(value_); + Function function(BOOST_ASIO_MOVE_CAST(Function)(function_)); + spawned_coroutine_thread spawned_thread(caller); + *spawned_thread_out_ = &spawned_thread; + spawned_thread_out_ = 0; + spawned_thread.suspend(); + function(&spawned_thread); + spawned_thread.suspend(); } private: - completion_handler_type& handler_; - typename basic_yield_context::caller_type& ca_; - atomic_count ready_; - boost::system::error_code* out_ec_; - boost::system::error_code ec_; - return_type value_; + Function function_; + spawned_coroutine_thread** spawned_thread_out_; }; - template - class coro_async_result + 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(BOOST_ASIO_MOVE_ARG(fiber_type) caller) + : caller_(BOOST_ASIO_MOVE_CAST(fiber_type)(caller)), + on_suspend_fn_(0), + on_suspend_arg_(0) + { + } + + template + static spawned_thread_base* spawn(allocator_arg_t, + BOOST_ASIO_MOVE_ARG(StackAllocator) stack_allocator, + BOOST_ASIO_MOVE_ARG(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(), + BOOST_ASIO_MOVE_CAST(StackAllocator)(stack_allocator), + entry_point::type>( + BOOST_ASIO_MOVE_CAST(F)(f), &spawned_thread)); + callee = BOOST_ASIO_MOVE_CAST(fiber_type)(callee).resume(); + spawned_thread->callee_ = BOOST_ASIO_MOVE_CAST(fiber_type)(callee); + spawned_thread->parent_cancellation_slot_ = parent_cancel_slot; + spawned_thread->cancellation_state_ = cancel_state; + return spawned_thread; + } + + template + static spawned_thread_base* spawn(BOOST_ASIO_MOVE_ARG(F) f, + cancellation_slot parent_cancel_slot = cancellation_slot(), + cancellation_state cancel_state = cancellation_state()) + { + return spawn(allocator_arg_t(), boost::context::fixedsize_stack(), + BOOST_ASIO_MOVE_CAST(F)(f), parent_cancel_slot, cancel_state); + } + + void resume() + { + callee_ = BOOST_ASIO_MOVE_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 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_ = BOOST_ASIO_MOVE_CAST(fiber_type)(caller_).resume(); + } + + void destroy() + { + fiber_type callee = BOOST_ASIO_MOVE_CAST(fiber_type)(callee_); + (void)callee; + } + +private: + template + class entry_point { public: - typedef coro_handler completion_handler_type; - typedef void return_type; - - explicit coro_async_result(completion_handler_type& h) - : handler_(h), - ca_(h.ca_), - ready_(2) + template + entry_point(BOOST_ASIO_MOVE_ARG(F) f, + spawned_fiber_thread** spawned_thread_out) + : function_(BOOST_ASIO_MOVE_CAST(F)(f)), + spawned_thread_out_(spawned_thread_out) { - h.ready_ = &ready_; - out_ec_ = h.ec_; - if (!out_ec_) h.ec_ = &ec_; } - void get() + fiber_type operator()(BOOST_ASIO_MOVE_ARG(fiber_type) caller) { - // Must not hold shared_ptr to coro while suspended. - handler_.coro_.reset(); - - if (--ready_ != 0) - ca_(); - if (!out_ec_ && ec_) throw boost::system::system_error(ec_); + Function function(BOOST_ASIO_MOVE_CAST(Function)(function_)); + spawned_fiber_thread spawned_thread( + BOOST_ASIO_MOVE_CAST(fiber_type)(caller)); + *spawned_thread_out_ = &spawned_thread; + spawned_thread_out_ = 0; + spawned_thread.suspend(); + function(&spawned_thread); + spawned_thread.suspend(); + return {}; } private: - completion_handler_type& handler_; - typename basic_yield_context::caller_type& ca_; - atomic_count ready_; - boost::system::error_code* out_ec_; - boost::system::error_code ec_; + 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 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(); +#if !defined(BOOST_ASIO_HAS_MOVE) + spawned_thread->attach(&spawned_thread_); +#endif // !defined(BOOST_ASIO_HAS_MOVE) + } + +#if defined(BOOST_ASIO_HAS_MOVE) + + spawned_thread_destroyer(spawned_thread_destroyer&& other) BOOST_ASIO_NOEXCEPT + : spawned_thread_(other.spawned_thread_) + { + other.spawned_thread_ = 0; + } + +#else // defined(BOOST_ASIO_HAS_MOVE) + + spawned_thread_destroyer( + const spawned_thread_destroyer& other) BOOST_ASIO_NOEXCEPT + : spawned_thread_(other.spawned_thread_) + { + spawned_thread_->detach(); + spawned_thread_->attach(&spawned_thread_); + } + +#endif // defined(BOOST_ASIO_HAS_MOVE) + + ~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) + : executor_(yield.executor_), + spawned_thread_(yield.spawned_thread_), + ec_(yield.ec_) + { + spawned_thread_->detach(); +#if !defined(BOOST_ASIO_HAS_MOVE) + spawned_thread_->attach(&spawned_thread_); +#endif // !defined(BOOST_ASIO_HAS_MOVE) + } + +#if defined(BOOST_ASIO_HAS_MOVE) + + spawn_handler_base(spawn_handler_base&& other) BOOST_ASIO_NOEXCEPT + : executor_(BOOST_ASIO_MOVE_CAST(Executor)(other.executor_)), + spawned_thread_(other.spawned_thread_), + ec_(other.ec_) + { + other.spawned_thread_ = 0; + other.ec_ = 0; + } + +#else // defined(BOOST_ASIO_HAS_MOVE) + + spawn_handler_base(const spawn_handler_base& other) BOOST_ASIO_NOEXCEPT + : executor_(other.executor_), + spawned_thread_(other.spawned_thread_), + ec_(other.ec_) + { + spawned_thread_->detach(); + spawned_thread_->attach(&spawned_thread_); + } + +#endif // defined(BOOST_ASIO_HAS_MOVE) + + ~spawn_handler_base() + { + if (spawned_thread_) + (post)(executor_, spawned_thread_destroyer(spawned_thread_)); + } + + executor_type get_executor() const BOOST_ASIO_NOEXCEPT + { + return executor_; + } + + cancellation_slot_type get_cancellation_slot() const BOOST_ASIO_NOEXCEPT + { + return spawned_thread_->get_cancellation_slot(); + } + + void resume() + { +#if defined(BOOST_ASIO_HAS_MOVE) + spawned_thread_->attach(&spawned_thread_); +#endif // defined(BOOST_ASIO_HAS_MOVE) + spawned_thread_->resume(); + } + +protected: + Executor executor_; + spawned_thread_base* spawned_thread_; + boost::system::error_code* ec_; +}; + +// Helper class to perform the initial resume on the correct executor. +template +class spawn_launcher + : public spawn_handler_base +{ +public: + spawn_launcher(const basic_yield_context& yield) + : spawn_handler_base(yield) + { + } + + void operator()() + { + this->resume(); + } +}; + +// 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->ec_) + { + *this->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 BOOST_ASIO_MOVE_CAST(return_type)(*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->ec_) + { + *this->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 BOOST_ASIO_MOVE_CAST(return_type)(*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 BOOST_ASIO_MOVE_CAST(return_type)(*result.value_); + } + +private: + result_type& result_; +}; + +#if defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES) \ + && defined(BOOST_ASIO_HAS_STD_TUPLE) + +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()(BOOST_ASIO_MOVE_ARG(Args)... args) + { + return_type value(BOOST_ASIO_MOVE_CAST(Args)(args)...); + result_ = &value; + this->resume(); + } + + static return_type on_resume(result_type& result) + { + return BOOST_ASIO_MOVE_CAST(return_type)(*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, + BOOST_ASIO_MOVE_ARG(Args)... args) + { + return_type value(BOOST_ASIO_MOVE_CAST(Args)(args)...); + if (this->ec_) + { + *this->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 BOOST_ASIO_MOVE_CAST(return_type)(*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, BOOST_ASIO_MOVE_ARG(Args)... args) + { + return_type value(BOOST_ASIO_MOVE_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 BOOST_ASIO_MOVE_CAST(return_type)(*result.value_); + } + +private: + result_type& result_; +}; + +#endif // defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES) + // && defined(BOOST_ASIO_HAS_STD_TUPLE) + +template +inline bool asio_handler_is_continuation(spawn_handler*) +{ + return true; +} + } // namespace detail -#if !defined(GENERATING_DOCUMENTATION) - -template -class async_result, ReturnType()> - : public detail::coro_async_result +template +class async_result, Signature> { public: - explicit async_result( - typename detail::coro_async_result::completion_handler_type& h) - : detail::coro_async_result(h) - { - } -}; + typedef typename detail::spawn_handler handler_type; + typedef typename handler_type::return_type return_type; -template -class async_result, ReturnType(Arg1)> - : public detail::coro_async_result::type> -{ -public: - explicit async_result( - typename detail::coro_async_result::type>::completion_handler_type& h) - : detail::coro_async_result::type>(h) - { - } -}; +#if defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES) -template -class async_result, - ReturnType(boost::system::error_code)> - : public detail::coro_async_result -{ -public: - explicit async_result( - typename detail::coro_async_result::completion_handler_type& h) - : detail::coro_async_result(h) + template + static return_type initiate(BOOST_ASIO_MOVE_ARG(Initiation) init, + const basic_yield_context& yield, + BOOST_ASIO_MOVE_ARG(InitArgs)... init_args) { - } -}; + typename handler_type::result_type result + = typename handler_type::result_type(); -template -class async_result, - ReturnType(boost::system::error_code, Arg2)> - : public detail::coro_async_result::type> -{ -public: - explicit async_result( - typename detail::coro_async_result::type>::completion_handler_type& h) - : detail::coro_async_result::type>(h) - { - } -}; + yield.spawned_thread_->suspend_with( + [&]() + { + BOOST_ASIO_MOVE_CAST(Initiation)(init)( + handler_type(yield, result), + BOOST_ASIO_MOVE_CAST(InitArgs)(init_args)...); + }); -template