summaryrefslogtreecommitdiffstats
path: root/src/spawn/include
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:54:28 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:54:28 +0000
commite6918187568dbd01842d8d1d2c808ce16a894239 (patch)
tree64f88b554b444a49f656b6c656111a145cbbaa28 /src/spawn/include
parentInitial commit. (diff)
downloadceph-upstream/18.2.2.tar.xz
ceph-upstream/18.2.2.zip
Adding upstream version 18.2.2.upstream/18.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/spawn/include')
-rw-r--r--src/spawn/include/spawn/detail/is_stack_allocator.hpp32
-rw-r--r--src/spawn/include/spawn/detail/net.hpp42
-rw-r--r--src/spawn/include/spawn/impl/spawn.hpp513
-rw-r--r--src/spawn/include/spawn/spawn.hpp292
4 files changed, 879 insertions, 0 deletions
diff --git a/src/spawn/include/spawn/detail/is_stack_allocator.hpp b/src/spawn/include/spawn/detail/is_stack_allocator.hpp
new file mode 100644
index 000000000..6f0d85426
--- /dev/null
+++ b/src/spawn/include/spawn/detail/is_stack_allocator.hpp
@@ -0,0 +1,32 @@
+//
+// detail/is_stack_allocator.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2019 Casey Bodley (cbodley at redhat 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)
+//
+
+#pragma once
+
+#include <type_traits>
+
+#include <boost/type_traits/make_void.hpp>
+
+namespace spawn {
+namespace detail {
+
+template <typename T, typename = void>
+struct is_stack_allocator : std::false_type {};
+
+template <typename T>
+struct is_stack_allocator<T, boost::void_t<decltype(
+ // boost::context::stack_context c = salloc.allocate();
+ std::declval<boost::context::stack_context>() = std::declval<T&>().allocate(),
+ // salloc.deallocate(c);
+ std::declval<T&>().deallocate(std::declval<boost::context::stack_context&>())
+ )>> : std::true_type {};
+
+} // namespace detail
+} // namespace spawn
diff --git a/src/spawn/include/spawn/detail/net.hpp b/src/spawn/include/spawn/detail/net.hpp
new file mode 100644
index 000000000..9c2c7a2cc
--- /dev/null
+++ b/src/spawn/include/spawn/detail/net.hpp
@@ -0,0 +1,42 @@
+//
+// net.hpp
+// ~~~~~~~~~
+//
+// Copyright (c) 2019 Casey Bodley (cbodley at redhat 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)
+//
+
+#pragma once
+
+#include <boost/asio/associated_allocator.hpp>
+#include <boost/asio/associated_executor.hpp>
+#include <boost/asio/async_result.hpp>
+#include <boost/asio/bind_executor.hpp>
+#include <boost/asio/executor.hpp>
+#include <boost/asio/is_executor.hpp>
+#include <boost/asio/strand.hpp>
+
+#define SPAWN_NET_NAMESPACE boost::asio
+
+namespace spawn {
+namespace detail {
+namespace net {
+
+using boost::asio::associated_executor_t;
+using boost::asio::get_associated_executor;
+
+using boost::asio::associated_allocator_t;
+using boost::asio::get_associated_allocator;
+
+using boost::asio::execution_context;
+using boost::asio::executor;
+using boost::asio::executor_binder;
+using boost::asio::is_executor;
+
+using boost::asio::strand;
+
+} // namespace net
+} // namespace detail
+} // namespace spawn
diff --git a/src/spawn/include/spawn/impl/spawn.hpp b/src/spawn/include/spawn/impl/spawn.hpp
new file mode 100644
index 000000000..649e830ae
--- /dev/null
+++ b/src/spawn/include/spawn/impl/spawn.hpp
@@ -0,0 +1,513 @@
+//
+// impl/spawn.hpp
+// ~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2017 Oliver Kowalke (oliver dot kowalke at gmail dot com)
+// Copyright (c) 2019 Casey Bodley (cbodley at redhat 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)
+//
+
+#pragma once
+
+#include <atomic>
+#include <memory>
+#include <tuple>
+
+#include <boost/system/system_error.hpp>
+#include <boost/context/continuation.hpp>
+#include <boost/optional.hpp>
+
+#include <spawn/detail/net.hpp>
+#include <spawn/detail/is_stack_allocator.hpp>
+
+namespace spawn {
+namespace detail {
+
+ class continuation_context
+ {
+ public:
+ boost::context::continuation context_;
+ std::exception_ptr eptr_;
+
+ void resume()
+ {
+ context_ = context_.resume();
+
+ if (eptr_)
+ std::rethrow_exception(std::move(eptr_));
+ }
+ };
+
+ template <typename Handler, typename ...Ts>
+ class coro_handler
+ {
+ public:
+ coro_handler(basic_yield_context<Handler> ctx)
+ : callee_(ctx.callee_.lock()),
+ caller_(ctx.caller_),
+ handler_(ctx.handler_),
+ ready_(0),
+ ec_(ctx.ec_),
+ value_(0)
+ {
+ }
+
+ void operator()(Ts... values)
+ {
+ *ec_ = boost::system::error_code();
+ *value_ = std::forward_as_tuple(std::move(values)...);
+ if (--*ready_ == 0)
+ callee_->resume();
+ }
+
+ void operator()(boost::system::error_code ec, Ts... values)
+ {
+ *ec_ = ec;
+ *value_ = std::forward_as_tuple(std::move(values)...);
+ if (--*ready_ == 0)
+ callee_->resume();
+ }
+
+ //private:
+ std::shared_ptr<continuation_context> callee_;
+ continuation_context& caller_;
+ Handler handler_;
+ std::atomic<long>* ready_;
+ boost::system::error_code* ec_;
+ boost::optional<std::tuple<Ts...>>* value_;
+ };
+
+ template <typename Handler, typename T>
+ class coro_handler<Handler, T>
+ {
+ public:
+ coro_handler(basic_yield_context<Handler> ctx)
+ : callee_(ctx.callee_.lock()),
+ caller_(ctx.caller_),
+ handler_(ctx.handler_),
+ ready_(0),
+ ec_(ctx.ec_),
+ value_(0)
+ {
+ }
+
+ void operator()(T value)
+ {
+ *ec_ = boost::system::error_code();
+ *value_ = std::move(value);
+ if (--*ready_ == 0)
+ callee_->resume();
+ }
+
+ void operator()(boost::system::error_code ec, T value)
+ {
+ *ec_ = ec;
+ *value_ = std::move(value);
+ if (--*ready_ == 0)
+ callee_->resume();
+ }
+
+ //private:
+ std::shared_ptr<continuation_context> callee_;
+ continuation_context& caller_;
+ Handler handler_;
+ std::atomic<long>* ready_;
+ boost::system::error_code* ec_;
+ boost::optional<T>* value_;
+ };
+
+ template <typename Handler>
+ class coro_handler<Handler, void>
+ {
+ public:
+ coro_handler(basic_yield_context<Handler> ctx)
+ : callee_(ctx.callee_.lock()),
+ caller_(ctx.caller_),
+ handler_(ctx.handler_),
+ ready_(0),
+ ec_(ctx.ec_)
+ {
+ }
+
+ void operator()()
+ {
+ *ec_ = boost::system::error_code();
+ if (--*ready_ == 0)
+ callee_->resume();
+ }
+
+ void operator()(boost::system::error_code ec)
+ {
+ *ec_ = ec;
+ if (--*ready_ == 0)
+ callee_->resume();
+ }
+
+ //private:
+ std::shared_ptr<continuation_context> callee_;
+ continuation_context& caller_;
+ Handler handler_;
+ std::atomic<long>* ready_;
+ boost::system::error_code* ec_;
+ };
+
+ template <typename Handler, typename ...Ts>
+ class coro_async_result
+ {
+ public:
+ using completion_handler_type = coro_handler<Handler, Ts...>;
+ using return_type = std::tuple<Ts...>;
+
+ explicit coro_async_result(completion_handler_type& h)
+ : handler_(h),
+ caller_(h.caller_),
+ 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 while suspended.
+ handler_.callee_.reset();
+
+ if (--ready_ != 0)
+ caller_.resume(); // suspend caller
+ if (!out_ec_ && ec_) throw boost::system::system_error(ec_);
+ return std::move(*value_);
+ }
+
+ private:
+ completion_handler_type& handler_;
+ continuation_context& caller_;
+ std::atomic<long> ready_;
+ boost::system::error_code* out_ec_;
+ boost::system::error_code ec_;
+ boost::optional<return_type> value_;
+ };
+
+ template <typename Handler, typename T>
+ class coro_async_result<Handler, T>
+ {
+ public:
+ using completion_handler_type = coro_handler<Handler, T>;
+ using return_type = T;
+
+ explicit coro_async_result(completion_handler_type& h)
+ : handler_(h),
+ caller_(h.caller_),
+ 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 while suspended.
+ handler_.callee_.reset();
+
+ if (--ready_ != 0)
+ caller_.resume(); // suspend caller
+ if (!out_ec_ && ec_) throw boost::system::system_error(ec_);
+ return std::move(*value_);
+ }
+
+ private:
+ completion_handler_type& handler_;
+ continuation_context& caller_;
+ std::atomic<long> ready_;
+ boost::system::error_code* out_ec_;
+ boost::system::error_code ec_;
+ boost::optional<return_type> value_;
+ };
+
+ template <typename Handler>
+ class coro_async_result<Handler, void>
+ {
+ public:
+ using completion_handler_type = coro_handler<Handler, void>;
+ using return_type = void;
+
+ explicit coro_async_result(completion_handler_type& h)
+ : handler_(h),
+ caller_(h.caller_),
+ ready_(2)
+ {
+ h.ready_ = &ready_;
+ out_ec_ = h.ec_;
+ if (!out_ec_) h.ec_ = &ec_;
+ }
+
+ void get()
+ {
+ // Must not hold shared_ptr while suspended.
+ handler_.callee_.reset();
+
+ if (--ready_ != 0)
+ caller_.resume(); // suspend caller
+ if (!out_ec_ && ec_) throw boost::system::system_error(ec_);
+ }
+
+ private:
+ completion_handler_type& handler_;
+ continuation_context& caller_;
+ std::atomic<long> ready_;
+ boost::system::error_code* out_ec_;
+ boost::system::error_code ec_;
+ };
+
+} // namespace detail
+} // namespace spawn
+
+#if !defined(GENERATING_DOCUMENTATION)
+
+template <typename Handler, typename ReturnType>
+class SPAWN_NET_NAMESPACE::async_result<spawn::basic_yield_context<Handler>, ReturnType()>
+ : public spawn::detail::coro_async_result<Handler, void>
+{
+public:
+ explicit async_result(
+ typename spawn::detail::coro_async_result<Handler,
+ void>::completion_handler_type& h)
+ : spawn::detail::coro_async_result<Handler, void>(h)
+ {
+ }
+};
+
+template <typename Handler, typename ReturnType, typename ...Args>
+class SPAWN_NET_NAMESPACE::async_result<spawn::basic_yield_context<Handler>, ReturnType(Args...)>
+ : public spawn::detail::coro_async_result<Handler, typename std::decay<Args>::type...>
+{
+public:
+ explicit async_result(
+ typename spawn::detail::coro_async_result<Handler,
+ typename std::decay<Args>::type...>::completion_handler_type& h)
+ : spawn::detail::coro_async_result<Handler, typename std::decay<Args>::type...>(h)
+ {
+ }
+};
+
+template <typename Handler, typename ReturnType>
+class SPAWN_NET_NAMESPACE::async_result<spawn::basic_yield_context<Handler>,
+ ReturnType(boost::system::error_code)>
+ : public spawn::detail::coro_async_result<Handler, void>
+{
+public:
+ explicit async_result(
+ typename spawn::detail::coro_async_result<Handler,
+ void>::completion_handler_type& h)
+ : spawn::detail::coro_async_result<Handler, void>(h)
+ {
+ }
+};
+
+template <typename Handler, typename ReturnType, typename ...Args>
+class SPAWN_NET_NAMESPACE::async_result<spawn::basic_yield_context<Handler>,
+ ReturnType(boost::system::error_code, Args...)>
+ : public spawn::detail::coro_async_result<Handler, typename std::decay<Args>::type...>
+{
+public:
+ explicit async_result(
+ typename spawn::detail::coro_async_result<Handler,
+ typename std::decay<Args>::type...>::completion_handler_type& h)
+ : spawn::detail::coro_async_result<Handler, typename std::decay<Args>::type...>(h)
+ {
+ }
+};
+
+template <typename Handler, typename Allocator, typename ...Ts>
+struct SPAWN_NET_NAMESPACE::associated_allocator<spawn::detail::coro_handler<Handler, Ts...>, Allocator>
+{
+ using type = associated_allocator_t<Handler, Allocator>;
+
+ static type get(const spawn::detail::coro_handler<Handler, Ts...>& h,
+ const Allocator& a = Allocator()) noexcept
+ {
+ return associated_allocator<Handler, Allocator>::get(h.handler_, a);
+ }
+};
+
+template <typename Handler, typename Executor, typename ...Ts>
+struct SPAWN_NET_NAMESPACE::associated_executor<spawn::detail::coro_handler<Handler, Ts...>, Executor>
+{
+ using type = associated_executor_t<Handler, Executor>;
+
+ static type get(const spawn::detail::coro_handler<Handler, Ts...>& h,
+ const Executor& ex = Executor()) noexcept
+ {
+ return associated_executor<Handler, Executor>::get(h.handler_, ex);
+ }
+};
+
+namespace spawn {
+namespace detail {
+
+ template <typename Handler, typename Function, typename StackAllocator>
+ struct spawn_data
+ {
+ template <typename Hand, typename Func, typename Stack>
+ spawn_data(Hand&& handler, bool call_handler, Func&& function, Stack&& salloc)
+ : handler_(std::forward<Hand>(handler)),
+ call_handler_(call_handler),
+ function_(std::forward<Func>(function)),
+ salloc_(std::forward<Stack>(salloc))
+ {
+ }
+ spawn_data(const spawn_data&) = delete;
+ spawn_data& operator=(const spawn_data&) = delete;
+
+ Handler handler_;
+ bool call_handler_;
+ Function function_;
+ StackAllocator salloc_;
+ continuation_context caller_;
+ };
+
+ template <typename Handler, typename Function, typename StackAllocator>
+ struct spawn_helper
+ {
+ void operator()()
+ {
+ callee_.reset(new continuation_context());
+ callee_->context_ = boost::context::callcc(
+ std::allocator_arg, std::move(data_->salloc_),
+ [this] (boost::context::continuation&& c)
+ {
+ std::shared_ptr<spawn_data<Handler, Function, StackAllocator> > data = data_;
+ data->caller_.context_ = std::move(c);
+ const basic_yield_context<Handler> yh(callee_, data->caller_, data->handler_);
+ try
+ {
+ (data->function_)(yh);
+ if (data->call_handler_)
+ {
+ (data->handler_)();
+ }
+ }
+ catch (const boost::context::detail::forced_unwind& e)
+ {
+ throw; // must allow forced_unwind to propagate
+ }
+ catch (...)
+ {
+ auto callee = yh.callee_.lock();
+ if (callee)
+ callee->eptr_ = std::current_exception();
+ }
+ boost::context::continuation caller = std::move(data->caller_.context_);
+ data.reset();
+ return caller;
+ });
+ if (callee_->eptr_)
+ std::rethrow_exception(std::move(callee_->eptr_));
+ }
+
+ std::shared_ptr<continuation_context> callee_;
+ std::shared_ptr<spawn_data<Handler, Function, StackAllocator> > data_;
+ };
+
+ inline void default_spawn_handler() {}
+
+} // namespace detail
+
+template <typename Function, typename StackAllocator>
+auto spawn(Function&& function, StackAllocator&& salloc)
+ -> typename std::enable_if<detail::is_stack_allocator<
+ typename std::decay<StackAllocator>::type>::value>::type
+{
+ auto ex = detail::net::get_associated_executor(function);
+
+ spawn(ex, std::forward<Function>(function),
+ std::forward<StackAllocator>(salloc));
+}
+
+template <typename Handler, typename Function, typename StackAllocator>
+auto spawn(Handler&& handler, Function&& function, StackAllocator&& salloc)
+ -> typename std::enable_if<!detail::net::is_executor<typename std::decay<Handler>::type>::value &&
+ !std::is_convertible<Handler&, detail::net::execution_context&>::value &&
+ !detail::is_stack_allocator<typename std::decay<Function>::type>::value &&
+ detail::is_stack_allocator<typename std::decay<StackAllocator>::type>::value>::type
+{
+ using handler_type = typename std::decay<Handler>::type;
+ using function_type = typename std::decay<Function>::type;
+
+ auto ex = detail::net::get_associated_executor(handler);
+ auto a = detail::net::get_associated_allocator(handler);
+
+ detail::spawn_helper<handler_type, function_type, StackAllocator> helper;
+ helper.data_ = std::make_shared<
+ detail::spawn_data<handler_type, function_type, StackAllocator> >(
+ std::forward<Handler>(handler), true,
+ std::forward<Function>(function),
+ std::forward<StackAllocator>(salloc));
+
+ ex.dispatch(helper, a);
+}
+
+template <typename Handler, typename Function, typename StackAllocator>
+auto spawn(basic_yield_context<Handler> ctx, Function&& function,
+ StackAllocator&& salloc)
+ -> typename std::enable_if<detail::is_stack_allocator<
+ typename std::decay<StackAllocator>::type>::value>::type
+{
+ using function_type = typename std::decay<Function>::type;
+
+ Handler handler(ctx.handler_); // Explicit copy that might be moved from.
+
+ auto ex = detail::net::get_associated_executor(handler);
+ auto a = detail::net::get_associated_allocator(handler);
+
+ detail::spawn_helper<Handler, function_type, StackAllocator> helper;
+ helper.data_ = std::make_shared<
+ detail::spawn_data<Handler, function_type, StackAllocator> >(
+ std::forward<Handler>(handler), false,
+ std::forward<Function>(function),
+ std::forward<StackAllocator>(salloc));
+
+ ex.dispatch(helper, a);
+}
+
+template <typename Function, typename Executor, typename StackAllocator>
+auto spawn(const Executor& ex, Function&& function, StackAllocator&& salloc)
+ -> typename std::enable_if<detail::net::is_executor<Executor>::value &&
+ detail::is_stack_allocator<typename std::decay<StackAllocator>::type>::value>::type
+{
+ spawn(detail::net::strand<Executor>(ex),
+ std::forward<Function>(function),
+ std::forward<StackAllocator>(salloc));
+}
+
+template <typename Function, typename Executor, typename StackAllocator>
+auto spawn(const detail::net::strand<Executor>& ex,
+ Function&& function, StackAllocator&& salloc)
+ -> typename std::enable_if<detail::is_stack_allocator<
+ typename std::decay<StackAllocator>::type>::value>::type
+{
+ spawn(bind_executor(ex, &detail::default_spawn_handler),
+ std::forward<Function>(function),
+ std::forward<StackAllocator>(salloc));
+}
+
+template <typename Function, typename ExecutionContext, typename StackAllocator>
+auto spawn(ExecutionContext& ctx, Function&& function, StackAllocator&& salloc)
+ -> typename std::enable_if<std::is_convertible<
+ ExecutionContext&, detail::net::execution_context&>::value &&
+ detail::is_stack_allocator<typename std::decay<StackAllocator>::type>::value>::type
+{
+ spawn(ctx.get_executor(),
+ std::forward<Function>(function),
+ std::forward<StackAllocator>(salloc));
+}
+
+#endif // !defined(GENERATING_DOCUMENTATION)
+
+} // namespace spawn
diff --git a/src/spawn/include/spawn/spawn.hpp b/src/spawn/include/spawn/spawn.hpp
new file mode 100644
index 000000000..a9b4d3c63
--- /dev/null
+++ b/src/spawn/include/spawn/spawn.hpp
@@ -0,0 +1,292 @@
+//
+// spawn.hpp
+// ~~~~~~~~~
+//
+// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2017 Oliver Kowalke (oliver dot kowalke at gmail dot com)
+// Copyright (c) 2019 Casey Bodley (cbodley at redhat 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)
+//
+
+#pragma once
+
+#include <memory>
+
+#include <boost/context/fixedsize_stack.hpp>
+#include <boost/context/segmented_stack.hpp>
+#include <boost/system/system_error.hpp>
+
+#include <spawn/detail/net.hpp>
+#include <spawn/detail/is_stack_allocator.hpp>
+
+namespace spawn {
+namespace detail {
+
+ class continuation_context;
+
+} // namespace detail
+
+/// Context object represents the current execution context.
+/**
+ * The basic_yield_context class is used to represent the current execution
+ * context. A basic_yield_context may be passed as a handler to an
+ * asynchronous operation. For example:
+ *
+ * @code template <typename Handler>
+ * void my_continuation(basic_yield_context<Handler> yield)
+ * {
+ * ...
+ * std::size_t n = my_socket.async_read_some(buffer, yield);
+ * ...
+ * } @endcode
+ *
+ * The initiating function (async_read_some in the above example) suspends the
+ * current execution context, e.g. reifies a continuation. The continuation
+ * is resumed when the asynchronous operation completes, and the result of
+ * the operation is returned.
+ */
+template <typename Handler>
+class basic_yield_context
+{
+public:
+ /// Construct a yield context to represent the specified execution context.
+ /**
+ * Most applications do not need to use this constructor. Instead, the
+ * spawn() function passes a yield context as an argument to the continuation
+ * function.
+ */
+ basic_yield_context(
+ const std::weak_ptr<detail::continuation_context>& callee,
+ detail::continuation_context& caller, Handler& handler)
+ : callee_(callee),
+ caller_(caller),
+ handler_(handler),
+ ec_(0)
+ {
+ }
+
+ /// Construct a yield context from another yield context type.
+ /**
+ * Requires that OtherHandler be convertible to Handler.
+ */
+ template <typename OtherHandler>
+ basic_yield_context(const basic_yield_context<OtherHandler>& other)
+ : callee_(other.callee_),
+ caller_(other.caller_),
+ handler_(other.handler_),
+ ec_(other.ec_)
+ {
+ }
+
+ /// Return a yield context that sets the specified error_code.
+ /**
+ * By default, when a yield context is used with an asynchronous operation, a
+ * non-success error_code is converted to system_error and thrown. This
+ * operator may be used to specify an error_code object that should instead be
+ * set with the asynchronous operation's result. For example:
+ *
+ * @code template <typename Handler>
+ * void my_continuation(basic_yield_context<Handler> yield)
+ * {
+ * ...
+ * std::size_t n = my_socket.async_read_some(buffer, yield[ec]);
+ * if (ec)
+ * {
+ * // An error occurred.
+ * }
+ * ...
+ * } @endcode
+ */
+ basic_yield_context operator[](boost::system::error_code& ec) const
+ {
+ basic_yield_context tmp(*this);
+ tmp.ec_ = &ec;
+ return tmp;
+ }
+
+#if defined(GENERATING_DOCUMENTATION)
+private:
+#endif // defined(GENERATING_DOCUMENTATION)
+ std::weak_ptr<detail::continuation_context> callee_;
+ detail::continuation_context& caller_;
+ Handler handler_;
+ boost::system::error_code* ec_;
+};
+
+#if defined(GENERATING_DOCUMENTATION)
+/// Context object that represents the current execution context.
+using yield_context = basic_yield_context<unspecified>;
+#else // defined(GENERATING_DOCUMENTATION)
+using yield_context = basic_yield_context<
+ detail::net::executor_binder<void(*)(), detail::net::executor>>;
+#endif // defined(GENERATING_DOCUMENTATION)
+
+/**
+ * @defgroup spawn spawn::spawn
+ *
+ * @brief Start a new execution context with a new stack.
+ *
+ * The spawn() function is a high-level wrapper over the Boost.Context
+ * library (callcc()/continuation). This function enables programs to
+ * implement asynchronous logic in a synchronous manner, as illustrated
+ * by the following example:
+ *
+ * @code spawn::spawn(my_strand, do_echo);
+ *
+ * // ...
+ *
+ * void do_echo(spawn::yield_context yield)
+ * {
+ * try
+ * {
+ * char data[128];
+ * for (;;)
+ * {
+ * std::size_t length =
+ * my_socket.async_read_some(
+ * boost::asio::buffer(data), yield);
+ *
+ * boost::asio::async_write(my_socket,
+ * boost::asio::buffer(data, length), yield);
+ * }
+ * }
+ * catch (std::exception& e)
+ * {
+ * // ...
+ * }
+ * } @endcode
+ */
+/*@{*/
+
+/// Start a new execution context (with new stack), calling the specified handler
+/// when it completes.
+/**
+ * This function is used to launch a new execution context on behalf of callcc()
+ * and continuation.
+ *
+ * @param function The continuation function. The function must have the signature:
+ * @code void function(basic_yield_context<Handler> yield); @endcode
+ *
+ * @param salloc Boost.Context uses stack allocators to create stacks.
+ */
+template <typename Function, typename StackAllocator = boost::context::default_stack>
+auto spawn(Function&& function, StackAllocator&& salloc = StackAllocator())
+ -> typename std::enable_if<detail::is_stack_allocator<
+ typename std::decay<StackAllocator>::type>::value>::type;
+
+/// Start a new execution context (with new stack), calling the specified handler
+/// when it completes.
+/**
+ * This function is used to launch a new execution context on behalf of callcc()
+ * and continuation.
+ *
+ * @param handler A handler to be called when the continuation exits. More
+ * importantly, the handler provides an execution context (via the the handler
+ * invocation hook) for the continuation. The handler must have the signature:
+ * @code void handler(); @endcode
+ *
+ * @param function The continuation function. The function must have the signature:
+ * @code void function(basic_yield_context<Handler> yield); @endcode
+ *
+ * @param salloc Boost.Context uses stack allocators to create stacks.
+ */
+template <typename Handler, typename Function,
+ typename StackAllocator = boost::context::default_stack>
+auto spawn(Handler&& handler, Function&& function,
+ StackAllocator&& salloc = StackAllocator())
+ -> typename std::enable_if<!detail::net::is_executor<typename std::decay<Handler>::type>::value &&
+ !std::is_convertible<Handler&, detail::net::execution_context&>::value &&
+ !detail::is_stack_allocator<typename std::decay<Function>::type>::value &&
+ detail::is_stack_allocator<typename std::decay<StackAllocator>::type>::value>::type;
+
+/// Start a new execution context (with new stack), inheriting the execution context of another.
+/**
+ * This function is used to launch a new execution context on behalf of callcc()
+ * and continuation.
+ *
+ * @param ctx Identifies the current execution context as a parent of the new
+ * continuation. This specifies that the new continuation should inherit the
+ * execution context of the parent. For example, if the parent continuation is
+ * executing in a particular strand, then the new continuation will execute in the
+ * same strand.
+ *
+ * @param function The continuation function. The function must have the signature:
+ * @code void function(basic_yield_context<Handler> yield); @endcode
+ *
+ * @param salloc Boost.Context uses stack allocators to create stacks.
+ */
+template <typename Handler, typename Function,
+ typename StackAllocator = boost::context::default_stack>
+auto spawn(basic_yield_context<Handler> ctx, Function&& function,
+ StackAllocator&& salloc = StackAllocator())
+ -> typename std::enable_if<detail::is_stack_allocator<
+ typename std::decay<StackAllocator>::type>::value>::type;
+
+/// Start a new execution context (with new stack) that executes on a given executor.
+/**
+ * This function is used to launch a new execution context on behalf of callcc()
+ * and continuation.
+ *
+ * @param ex Identifies the executor that will run the continuation. The new
+ * continuation is implicitly given its own strand within this executor.
+ *
+ * @param function The continuations function. The function must have the signature:
+ * @code void function(yield_context yield); @endcode
+ *
+ * @param salloc Boost.Context uses stack allocators to create stacks.
+ */
+template <typename Function, typename Executor,
+ typename StackAllocator = boost::context::default_stack>
+auto spawn(const Executor& ex, Function&& function,
+ StackAllocator&& salloc = StackAllocator())
+ -> typename std::enable_if<detail::net::is_executor<Executor>::value &&
+ detail::is_stack_allocator<typename std::decay<StackAllocator>::type>::value>::type;
+
+/// Start a new execution context (with new stack) that executes on a given strand.
+/**
+ * This function is used to launch a new execution context on behalf of callcc()
+ * and continuation.
+ *
+ * @param ex Identifies the strand that will run the continuation.
+ *
+ * @param function The continuation function. The function must have the signature:
+ * @code void function(yield_context yield); @endcode
+ *
+ * @param salloc Boost.Context uses stack allocators to create stacks.
+ */
+template <typename Function, typename Executor,
+ typename StackAllocator = boost::context::default_stack>
+auto spawn(const detail::net::strand<Executor>& ex,
+ Function&& function, StackAllocator&& salloc = StackAllocator())
+ -> typename std::enable_if<detail::is_stack_allocator<
+ typename std::decay<StackAllocator>::type>::value>::type;
+
+/// Start a new stackful context (with new stack) that executes on a given execution context.
+/**
+ * This function is used to launch a new execution context on behalf of callcc()
+ * and continuation.
+ *
+ * @param ctx Identifies the execution context that will run the continuation. The
+ * new continuation is implicitly given its own strand within this execution
+ * context.
+ *
+ * @param function The continuation function. The function must have the signature:
+ * @code void function(yield_context yield); @endcode
+ *
+ * @param salloc Boost.Context uses stack allocators to create stacks.
+ */
+template <typename Function, typename ExecutionContext,
+ typename StackAllocator = boost::context::default_stack>
+auto spawn(ExecutionContext& ctx, Function&& function,
+ StackAllocator&& salloc = StackAllocator())
+ -> typename std::enable_if<std::is_convertible<
+ ExecutionContext&, detail::net::execution_context&>::value &&
+ detail::is_stack_allocator<typename std::decay<StackAllocator>::type>::value>::type;
+
+/*@}*/
+
+} // namespace spawn
+
+#include <spawn/impl/spawn.hpp>