5 // Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
6 // Copyright (c) 2017 Oliver Kowalke (oliver dot kowalke at gmail dot com)
7 // Copyright (c) 2019 Casey Bodley (cbodley at redhat dot com)
9 // Distributed under the Boost Software License, Version 1.0. (See accompanying
10 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
17 #include <boost/context/fixedsize_stack.hpp>
18 #include <boost/context/segmented_stack.hpp>
19 #include <boost/system/system_error.hpp>
21 #include <spawn/detail/net.hpp>
22 #include <spawn/detail/is_stack_allocator.hpp>
27 class continuation_context;
31 /// Context object represents the current execution context.
33 * The basic_yield_context class is used to represent the current execution
34 * context. A basic_yield_context may be passed as a handler to an
35 * asynchronous operation. For example:
37 * @code template <typename Handler>
38 * void my_continuation(basic_yield_context<Handler> yield)
41 * std::size_t n = my_socket.async_read_some(buffer, yield);
45 * The initiating function (async_read_some in the above example) suspends the
46 * current execution context, e.g. reifies a continuation. The continuation
47 * is resumed when the asynchronous operation completes, and the result of
48 * the operation is returned.
50 template <typename Handler>
51 class basic_yield_context
54 /// Construct a yield context to represent the specified execution context.
56 * Most applications do not need to use this constructor. Instead, the
57 * spawn() function passes a yield context as an argument to the continuation
61 const std::weak_ptr<detail::continuation_context>& callee,
62 detail::continuation_context& caller, Handler& handler)
70 /// Construct a yield context from another yield context type.
72 * Requires that OtherHandler be convertible to Handler.
74 template <typename OtherHandler>
75 basic_yield_context(const basic_yield_context<OtherHandler>& other)
76 : callee_(other.callee_),
77 caller_(other.caller_),
78 handler_(other.handler_),
83 /// Return a yield context that sets the specified error_code.
85 * By default, when a yield context is used with an asynchronous operation, a
86 * non-success error_code is converted to system_error and thrown. This
87 * operator may be used to specify an error_code object that should instead be
88 * set with the asynchronous operation's result. For example:
90 * @code template <typename Handler>
91 * void my_continuation(basic_yield_context<Handler> yield)
94 * std::size_t n = my_socket.async_read_some(buffer, yield[ec]);
97 * // An error occurred.
102 basic_yield_context operator[](boost::system::error_code& ec) const
104 basic_yield_context tmp(*this);
109 #if defined(GENERATING_DOCUMENTATION)
111 #endif // defined(GENERATING_DOCUMENTATION)
112 std::weak_ptr<detail::continuation_context> callee_;
113 detail::continuation_context& caller_;
115 boost::system::error_code* ec_;
118 #if defined(GENERATING_DOCUMENTATION)
119 /// Context object that represents the current execution context.
120 using yield_context = basic_yield_context<unspecified>;
121 #else // defined(GENERATING_DOCUMENTATION)
122 using yield_context = basic_yield_context<
123 detail::net::executor_binder<void(*)(), detail::net::executor>>;
124 #endif // defined(GENERATING_DOCUMENTATION)
127 * @defgroup spawn spawn::spawn
129 * @brief Start a new execution context with a new stack.
131 * The spawn() function is a high-level wrapper over the Boost.Context
132 * library (callcc()/continuation). This function enables programs to
133 * implement asynchronous logic in a synchronous manner, as illustrated
134 * by the following example:
136 * @code spawn::spawn(my_strand, do_echo);
140 * void do_echo(spawn::yield_context yield)
147 * std::size_t length =
148 * my_socket.async_read_some(
149 * boost::asio::buffer(data), yield);
151 * boost::asio::async_write(my_socket,
152 * boost::asio::buffer(data, length), yield);
155 * catch (std::exception& e)
163 /// Start a new execution context (with new stack), calling the specified handler
164 /// when it completes.
166 * This function is used to launch a new execution context on behalf of callcc()
169 * @param function The continuation function. The function must have the signature:
170 * @code void function(basic_yield_context<Handler> yield); @endcode
172 * @param salloc Boost.Context uses stack allocators to create stacks.
174 template <typename Function, typename StackAllocator = boost::context::default_stack>
175 auto spawn(Function&& function, StackAllocator&& salloc = StackAllocator())
176 -> typename std::enable_if<detail::is_stack_allocator<
177 typename std::decay<StackAllocator>::type>::value>::type;
179 /// Start a new execution context (with new stack), calling the specified handler
180 /// when it completes.
182 * This function is used to launch a new execution context on behalf of callcc()
185 * @param handler A handler to be called when the continuation exits. More
186 * importantly, the handler provides an execution context (via the the handler
187 * invocation hook) for the continuation. The handler must have the signature:
188 * @code void handler(); @endcode
190 * @param function The continuation function. The function must have the signature:
191 * @code void function(basic_yield_context<Handler> yield); @endcode
193 * @param salloc Boost.Context uses stack allocators to create stacks.
195 template <typename Handler, typename Function,
196 typename StackAllocator = boost::context::default_stack>
197 auto spawn(Handler&& handler, Function&& function,
198 StackAllocator&& salloc = StackAllocator())
199 -> typename std::enable_if<!detail::net::is_executor<typename std::decay<Handler>::type>::value &&
200 !std::is_convertible<Handler&, detail::net::execution_context&>::value &&
201 !detail::is_stack_allocator<typename std::decay<Function>::type>::value &&
202 detail::is_stack_allocator<typename std::decay<StackAllocator>::type>::value>::type;
204 /// Start a new execution context (with new stack), inheriting the execution context of another.
206 * This function is used to launch a new execution context on behalf of callcc()
209 * @param ctx Identifies the current execution context as a parent of the new
210 * continuation. This specifies that the new continuation should inherit the
211 * execution context of the parent. For example, if the parent continuation is
212 * executing in a particular strand, then the new continuation will execute in the
215 * @param function The continuation function. The function must have the signature:
216 * @code void function(basic_yield_context<Handler> yield); @endcode
218 * @param salloc Boost.Context uses stack allocators to create stacks.
220 template <typename Handler, typename Function,
221 typename StackAllocator = boost::context::default_stack>
222 auto spawn(basic_yield_context<Handler> ctx, Function&& function,
223 StackAllocator&& salloc = StackAllocator())
224 -> typename std::enable_if<detail::is_stack_allocator<
225 typename std::decay<StackAllocator>::type>::value>::type;
227 /// Start a new execution context (with new stack) that executes on a given executor.
229 * This function is used to launch a new execution context on behalf of callcc()
232 * @param ex Identifies the executor that will run the continuation. The new
233 * continuation is implicitly given its own strand within this executor.
235 * @param function The continuations function. The function must have the signature:
236 * @code void function(yield_context yield); @endcode
238 * @param salloc Boost.Context uses stack allocators to create stacks.
240 template <typename Function, typename Executor,
241 typename StackAllocator = boost::context::default_stack>
242 auto spawn(const Executor& ex, Function&& function,
243 StackAllocator&& salloc = StackAllocator())
244 -> typename std::enable_if<detail::net::is_executor<Executor>::value &&
245 detail::is_stack_allocator<typename std::decay<StackAllocator>::type>::value>::type;
247 /// Start a new execution context (with new stack) that executes on a given strand.
249 * This function is used to launch a new execution context on behalf of callcc()
252 * @param ex Identifies the strand that will run the continuation.
254 * @param function The continuation function. The function must have the signature:
255 * @code void function(yield_context yield); @endcode
257 * @param salloc Boost.Context uses stack allocators to create stacks.
259 template <typename Function, typename Executor,
260 typename StackAllocator = boost::context::default_stack>
261 auto spawn(const detail::net::strand<Executor>& ex,
262 Function&& function, StackAllocator&& salloc = StackAllocator())
263 -> typename std::enable_if<detail::is_stack_allocator<
264 typename std::decay<StackAllocator>::type>::value>::type;
266 /// Start a new stackful context (with new stack) that executes on a given execution context.
268 * This function is used to launch a new execution context on behalf of callcc()
271 * @param ctx Identifies the execution context that will run the continuation. The
272 * new continuation is implicitly given its own strand within this execution
275 * @param function The continuation function. The function must have the signature:
276 * @code void function(yield_context yield); @endcode
278 * @param salloc Boost.Context uses stack allocators to create stacks.
280 template <typename Function, typename ExecutionContext,
281 typename StackAllocator = boost::context::default_stack>
282 auto spawn(ExecutionContext& ctx, Function&& function,
283 StackAllocator&& salloc = StackAllocator())
284 -> typename std::enable_if<std::is_convertible<
285 ExecutionContext&, detail::net::execution_context&>::value &&
286 detail::is_stack_allocator<typename std::decay<StackAllocator>::type>::value>::type;
292 #include <spawn/impl/spawn.hpp>