]> git.proxmox.com Git - ceph.git/blob - ceph/src/spawn/include/spawn/spawn.hpp
import 15.2.0 Octopus source
[ceph.git] / ceph / src / spawn / include / spawn / spawn.hpp
1 //
2 // spawn.hpp
3 // ~~~~~~~~~
4 //
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)
8 //
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)
11 //
12
13 #pragma once
14
15 #include <memory>
16
17 #include <boost/context/fixedsize_stack.hpp>
18 #include <boost/context/segmented_stack.hpp>
19 #include <boost/system/system_error.hpp>
20
21 #include <spawn/detail/net.hpp>
22 #include <spawn/detail/is_stack_allocator.hpp>
23
24 namespace spawn {
25 namespace detail {
26
27 class continuation_context;
28
29 } // namespace detail
30
31 /// Context object represents the current execution context.
32 /**
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:
36 *
37 * @code template <typename Handler>
38 * void my_continuation(basic_yield_context<Handler> yield)
39 * {
40 * ...
41 * std::size_t n = my_socket.async_read_some(buffer, yield);
42 * ...
43 * } @endcode
44 *
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.
49 */
50 template <typename Handler>
51 class basic_yield_context
52 {
53 public:
54 /// Construct a yield context to represent the specified execution context.
55 /**
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
58 * function.
59 */
60 basic_yield_context(
61 const std::weak_ptr<detail::continuation_context>& callee,
62 detail::continuation_context& caller, Handler& handler)
63 : callee_(callee),
64 caller_(caller),
65 handler_(handler),
66 ec_(0)
67 {
68 }
69
70 /// Construct a yield context from another yield context type.
71 /**
72 * Requires that OtherHandler be convertible to Handler.
73 */
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_),
79 ec_(other.ec_)
80 {
81 }
82
83 /// Return a yield context that sets the specified error_code.
84 /**
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:
89 *
90 * @code template <typename Handler>
91 * void my_continuation(basic_yield_context<Handler> yield)
92 * {
93 * ...
94 * std::size_t n = my_socket.async_read_some(buffer, yield[ec]);
95 * if (ec)
96 * {
97 * // An error occurred.
98 * }
99 * ...
100 * } @endcode
101 */
102 basic_yield_context operator[](boost::system::error_code& ec) const
103 {
104 basic_yield_context tmp(*this);
105 tmp.ec_ = &ec;
106 return tmp;
107 }
108
109 #if defined(GENERATING_DOCUMENTATION)
110 private:
111 #endif // defined(GENERATING_DOCUMENTATION)
112 std::weak_ptr<detail::continuation_context> callee_;
113 detail::continuation_context& caller_;
114 Handler handler_;
115 boost::system::error_code* ec_;
116 };
117
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)
125
126 /**
127 * @defgroup spawn spawn::spawn
128 *
129 * @brief Start a new execution context with a new stack.
130 *
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:
135 *
136 * @code spawn::spawn(my_strand, do_echo);
137 *
138 * // ...
139 *
140 * void do_echo(spawn::yield_context yield)
141 * {
142 * try
143 * {
144 * char data[128];
145 * for (;;)
146 * {
147 * std::size_t length =
148 * my_socket.async_read_some(
149 * boost::asio::buffer(data), yield);
150 *
151 * boost::asio::async_write(my_socket,
152 * boost::asio::buffer(data, length), yield);
153 * }
154 * }
155 * catch (std::exception& e)
156 * {
157 * // ...
158 * }
159 * } @endcode
160 */
161 /*@{*/
162
163 /// Start a new execution context (with new stack), calling the specified handler
164 /// when it completes.
165 /**
166 * This function is used to launch a new execution context on behalf of callcc()
167 * and continuation.
168 *
169 * @param function The continuation function. The function must have the signature:
170 * @code void function(basic_yield_context<Handler> yield); @endcode
171 *
172 * @param salloc Boost.Context uses stack allocators to create stacks.
173 */
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;
178
179 /// Start a new execution context (with new stack), calling the specified handler
180 /// when it completes.
181 /**
182 * This function is used to launch a new execution context on behalf of callcc()
183 * and continuation.
184 *
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
189 *
190 * @param function The continuation function. The function must have the signature:
191 * @code void function(basic_yield_context<Handler> yield); @endcode
192 *
193 * @param salloc Boost.Context uses stack allocators to create stacks.
194 */
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;
203
204 /// Start a new execution context (with new stack), inheriting the execution context of another.
205 /**
206 * This function is used to launch a new execution context on behalf of callcc()
207 * and continuation.
208 *
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
213 * same strand.
214 *
215 * @param function The continuation function. The function must have the signature:
216 * @code void function(basic_yield_context<Handler> yield); @endcode
217 *
218 * @param salloc Boost.Context uses stack allocators to create stacks.
219 */
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;
226
227 /// Start a new execution context (with new stack) that executes on a given executor.
228 /**
229 * This function is used to launch a new execution context on behalf of callcc()
230 * and continuation.
231 *
232 * @param ex Identifies the executor that will run the continuation. The new
233 * continuation is implicitly given its own strand within this executor.
234 *
235 * @param function The continuations function. The function must have the signature:
236 * @code void function(yield_context yield); @endcode
237 *
238 * @param salloc Boost.Context uses stack allocators to create stacks.
239 */
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;
246
247 /// Start a new execution context (with new stack) that executes on a given strand.
248 /**
249 * This function is used to launch a new execution context on behalf of callcc()
250 * and continuation.
251 *
252 * @param ex Identifies the strand that will run the continuation.
253 *
254 * @param function The continuation function. The function must have the signature:
255 * @code void function(yield_context yield); @endcode
256 *
257 * @param salloc Boost.Context uses stack allocators to create stacks.
258 */
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;
265
266 /// Start a new stackful context (with new stack) that executes on a given execution context.
267 /**
268 * This function is used to launch a new execution context on behalf of callcc()
269 * and continuation.
270 *
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
273 * context.
274 *
275 * @param function The continuation function. The function must have the signature:
276 * @code void function(yield_context yield); @endcode
277 *
278 * @param salloc Boost.Context uses stack allocators to create stacks.
279 */
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;
287
288 /*@}*/
289
290 } // namespace spawn
291
292 #include <spawn/impl/spawn.hpp>