]> git.proxmox.com Git - ceph.git/blob - ceph/src/spawn/include/spawn/impl/spawn.hpp
import 15.2.0 Octopus source
[ceph.git] / ceph / src / spawn / include / spawn / impl / spawn.hpp
1 //
2 // impl/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 <atomic>
16 #include <memory>
17
18 #include <boost/system/system_error.hpp>
19 #include <boost/context/continuation.hpp>
20
21 #include <spawn/detail/net.hpp>
22 #include <spawn/detail/is_stack_allocator.hpp>
23
24 namespace spawn::detail {
25
26 class continuation_context
27 {
28 public:
29 boost::context::continuation context_;
30
31 continuation_context()
32 : context_()
33 {
34 }
35
36 void resume()
37 {
38 context_ = context_.resume();
39 }
40 };
41
42 template <typename Handler, typename T>
43 class coro_handler
44 {
45 public:
46 coro_handler(basic_yield_context<Handler> ctx)
47 : callee_(ctx.callee_.lock()),
48 caller_(ctx.caller_),
49 handler_(ctx.handler_),
50 ready_(0),
51 ec_(ctx.ec_),
52 value_(0)
53 {
54 }
55
56 void operator()(T value)
57 {
58 *ec_ = boost::system::error_code();
59 *value_ = std::move(value);
60 if (--*ready_ == 0)
61 callee_->resume();
62 }
63
64 void operator()(boost::system::error_code ec, T value)
65 {
66 *ec_ = ec;
67 *value_ = std::move(value);
68 if (--*ready_ == 0)
69 callee_->resume();
70 }
71
72 //private:
73 std::shared_ptr<continuation_context> callee_;
74 continuation_context& caller_;
75 Handler handler_;
76 std::atomic<long>* ready_;
77 boost::system::error_code* ec_;
78 T* value_;
79 };
80
81 template <typename Handler>
82 class coro_handler<Handler, void>
83 {
84 public:
85 coro_handler(basic_yield_context<Handler> ctx)
86 : callee_(ctx.callee_.lock()),
87 caller_(ctx.caller_),
88 handler_(ctx.handler_),
89 ready_(0),
90 ec_(ctx.ec_)
91 {
92 }
93
94 void operator()()
95 {
96 *ec_ = boost::system::error_code();
97 if (--*ready_ == 0)
98 callee_->resume();
99 }
100
101 void operator()(boost::system::error_code ec)
102 {
103 *ec_ = ec;
104 if (--*ready_ == 0)
105 callee_->resume();
106 }
107
108 //private:
109 std::shared_ptr<continuation_context> callee_;
110 continuation_context& caller_;
111 Handler handler_;
112 std::atomic<long>* ready_;
113 boost::system::error_code* ec_;
114 };
115
116 template <typename Handler, typename T>
117 class coro_async_result
118 {
119 public:
120 using completion_handler_type = coro_handler<Handler, T>;
121 using return_type = T;
122
123 explicit coro_async_result(completion_handler_type& h)
124 : handler_(h),
125 caller_(h.caller_),
126 ready_(2)
127 {
128 h.ready_ = &ready_;
129 out_ec_ = h.ec_;
130 if (!out_ec_) h.ec_ = &ec_;
131 h.value_ = &value_;
132 }
133
134 return_type get()
135 {
136 // Must not hold shared_ptr while suspended.
137 handler_.callee_.reset();
138
139 if (--ready_ != 0)
140 caller_.resume(); // suspend caller
141 if (!out_ec_ && ec_) throw boost::system::system_error(ec_);
142 return std::move(value_);
143 }
144
145 private:
146 completion_handler_type& handler_;
147 continuation_context& caller_;
148 std::atomic<long> ready_;
149 boost::system::error_code* out_ec_;
150 boost::system::error_code ec_;
151 return_type value_;
152 };
153
154 template <typename Handler>
155 class coro_async_result<Handler, void>
156 {
157 public:
158 using completion_handler_type = coro_handler<Handler, void>;
159 using return_type = void;
160
161 explicit coro_async_result(completion_handler_type& h)
162 : handler_(h),
163 caller_(h.caller_),
164 ready_(2)
165 {
166 h.ready_ = &ready_;
167 out_ec_ = h.ec_;
168 if (!out_ec_) h.ec_ = &ec_;
169 }
170
171 void get()
172 {
173 // Must not hold shared_ptr while suspended.
174 handler_.callee_.reset();
175
176 if (--ready_ != 0)
177 caller_.resume(); // suspend caller
178 if (!out_ec_ && ec_) throw boost::system::system_error(ec_);
179 }
180
181 private:
182 completion_handler_type& handler_;
183 continuation_context& caller_;
184 std::atomic<long> ready_;
185 boost::system::error_code* out_ec_;
186 boost::system::error_code ec_;
187 };
188
189 } // namespace spawn::detail
190
191 #if !defined(GENERATING_DOCUMENTATION)
192 namespace SPAWN_NET_NAMESPACE {
193
194 template <typename Handler, typename ReturnType>
195 class async_result<spawn::basic_yield_context<Handler>, ReturnType()>
196 : public spawn::detail::coro_async_result<Handler, void>
197 {
198 public:
199 explicit async_result(
200 typename spawn::detail::coro_async_result<Handler,
201 void>::completion_handler_type& h)
202 : spawn::detail::coro_async_result<Handler, void>(h)
203 {
204 }
205 };
206
207 template <typename Handler, typename ReturnType, typename Arg1>
208 class async_result<spawn::basic_yield_context<Handler>, ReturnType(Arg1)>
209 : public spawn::detail::coro_async_result<Handler, typename std::decay<Arg1>::type>
210 {
211 public:
212 explicit async_result(
213 typename spawn::detail::coro_async_result<Handler,
214 typename std::decay<Arg1>::type>::completion_handler_type& h)
215 : spawn::detail::coro_async_result<Handler, typename std::decay<Arg1>::type>(h)
216 {
217 }
218 };
219
220 template <typename Handler, typename ReturnType>
221 class async_result<spawn::basic_yield_context<Handler>,
222 ReturnType(boost::system::error_code)>
223 : public spawn::detail::coro_async_result<Handler, void>
224 {
225 public:
226 explicit async_result(
227 typename spawn::detail::coro_async_result<Handler,
228 void>::completion_handler_type& h)
229 : spawn::detail::coro_async_result<Handler, void>(h)
230 {
231 }
232 };
233
234 template <typename Handler, typename ReturnType, typename Arg2>
235 class async_result<spawn::basic_yield_context<Handler>,
236 ReturnType(boost::system::error_code, Arg2)>
237 : public spawn::detail::coro_async_result<Handler, typename std::decay<Arg2>::type>
238 {
239 public:
240 explicit async_result(
241 typename spawn::detail::coro_async_result<Handler,
242 typename std::decay<Arg2>::type>::completion_handler_type& h)
243 : spawn::detail::coro_async_result<Handler, typename std::decay<Arg2>::type>(h)
244 {
245 }
246 };
247
248 template <typename Handler, typename T, typename Allocator>
249 struct associated_allocator<spawn::detail::coro_handler<Handler, T>, Allocator>
250 {
251 using type = associated_allocator_t<Handler, Allocator>;
252
253 static type get(const spawn::detail::coro_handler<Handler, T>& h,
254 const Allocator& a = Allocator()) noexcept
255 {
256 return associated_allocator<Handler, Allocator>::get(h.handler_, a);
257 }
258 };
259
260 template <typename Handler, typename T, typename Executor>
261 struct associated_executor<spawn::detail::coro_handler<Handler, T>, Executor>
262 {
263 using type = associated_executor_t<Handler, Executor>;
264
265 static type get(const spawn::detail::coro_handler<Handler, T>& h,
266 const Executor& ex = Executor()) noexcept
267 {
268 return associated_executor<Handler, Executor>::get(h.handler_, ex);
269 }
270 };
271
272 } // namespace SPAWN_NET_NAMESPACE
273
274 namespace spawn {
275 namespace detail {
276
277 template <typename Handler, typename Function, typename StackAllocator>
278 struct spawn_data
279 {
280 template <typename Hand, typename Func, typename Stack>
281 spawn_data(Hand&& handler, bool call_handler, Func&& function, Stack&& salloc)
282 : handler_(std::forward<Hand>(handler)),
283 call_handler_(call_handler),
284 function_(std::forward<Func>(function)),
285 salloc_(std::forward<Stack>(salloc))
286 {
287 }
288 spawn_data(const spawn_data&) = delete;
289 spawn_data& operator=(const spawn_data&) = delete;
290
291 Handler handler_;
292 bool call_handler_;
293 Function function_;
294 StackAllocator salloc_;
295 continuation_context caller_;
296 };
297
298 template <typename Handler, typename Function, typename StackAllocator>
299 struct spawn_helper
300 {
301 void operator()()
302 {
303 callee_.reset(new continuation_context());
304 callee_->context_ = boost::context::callcc(
305 std::allocator_arg, std::move(data_->salloc_),
306 [this] (boost::context::continuation&& c)
307 {
308 std::shared_ptr<spawn_data<Handler, Function, StackAllocator> > data = data_;
309 data->caller_.context_ = std::move(c);
310 const basic_yield_context<Handler> yh(callee_, data->caller_, data->handler_);
311 (data->function_)(yh);
312 if (data->call_handler_)
313 {
314 (data->handler_)();
315 }
316 boost::context::continuation caller = std::move(data->caller_.context_);
317 data.reset();
318 return caller;
319 });
320 }
321
322 std::shared_ptr<continuation_context> callee_;
323 std::shared_ptr<spawn_data<Handler, Function, StackAllocator> > data_;
324 };
325
326 inline void default_spawn_handler() {}
327
328 } // namespace detail
329
330 template <typename Function, typename StackAllocator>
331 auto spawn(Function&& function, StackAllocator&& salloc)
332 -> typename std::enable_if<detail::is_stack_allocator<
333 typename std::decay<StackAllocator>::type>::value>::type
334 {
335 auto ex = detail::net::get_associated_executor(function);
336
337 spawn(ex, std::forward<Function>(function), salloc);
338 }
339
340 template <typename Handler, typename Function, typename StackAllocator>
341 auto spawn(Handler&& handler, Function&& function, StackAllocator&& salloc)
342 -> typename std::enable_if<!detail::net::is_executor<typename std::decay<Handler>::type>::value &&
343 !std::is_convertible<Handler&, detail::net::execution_context&>::value &&
344 !detail::is_stack_allocator<typename std::decay<Function>::type>::value &&
345 detail::is_stack_allocator<typename std::decay<StackAllocator>::type>::value>::type
346 {
347 using handler_type = typename std::decay<Handler>::type;
348 using function_type = typename std::decay<Function>::type;
349
350 auto ex = detail::net::get_associated_executor(handler);
351 auto a = detail::net::get_associated_allocator(handler);
352
353 detail::spawn_helper<handler_type, function_type, StackAllocator> helper;
354 helper.data_ = std::make_shared<
355 detail::spawn_data<handler_type, function_type, StackAllocator> >(
356 std::forward<Handler>(handler), true,
357 std::forward<Function>(function),
358 std::forward<StackAllocator>(salloc));
359
360 ex.dispatch(helper, a);
361 }
362
363 template <typename Handler, typename Function, typename StackAllocator>
364 auto spawn(basic_yield_context<Handler> ctx, Function&& function,
365 StackAllocator&& salloc)
366 -> typename std::enable_if<detail::is_stack_allocator<
367 typename std::decay<StackAllocator>::type>::value>::type
368 {
369 using function_type = typename std::decay<Function>::type;
370
371 Handler handler(ctx.handler_); // Explicit copy that might be moved from.
372
373 auto ex = detail::net::get_associated_executor(handler);
374 auto a = detail::net::get_associated_allocator(handler);
375
376 detail::spawn_helper<Handler, function_type, StackAllocator> helper;
377 helper.data_ = std::make_shared<
378 detail::spawn_data<Handler, function_type, StackAllocator> >(
379 std::forward<Handler>(handler), false,
380 std::forward<Function>(function),
381 std::forward<StackAllocator>(salloc));
382
383 ex.dispatch(helper, a);
384 }
385
386 template <typename Function, typename Executor, typename StackAllocator>
387 auto spawn(const Executor& ex, Function&& function, StackAllocator&& salloc)
388 -> typename std::enable_if<detail::net::is_executor<Executor>::value &&
389 detail::is_stack_allocator<typename std::decay<StackAllocator>::type>::value>::type
390 {
391 spawn(detail::net::strand<Executor>(ex),
392 std::forward<Function>(function),
393 std::forward<StackAllocator>(salloc));
394 }
395
396 template <typename Function, typename Executor, typename StackAllocator>
397 auto spawn(const detail::net::strand<Executor>& ex,
398 Function&& function, StackAllocator&& salloc)
399 -> typename std::enable_if<detail::is_stack_allocator<
400 typename std::decay<StackAllocator>::type>::value>::type
401 {
402 spawn(bind_executor(ex, &detail::default_spawn_handler),
403 std::forward<Function>(function),
404 std::forward<StackAllocator>(salloc));
405 }
406
407 template <typename Function, typename ExecutionContext, typename StackAllocator>
408 auto spawn(ExecutionContext& ctx, Function&& function, StackAllocator&& salloc)
409 -> typename std::enable_if<std::is_convertible<
410 ExecutionContext&, detail::net::execution_context&>::value &&
411 detail::is_stack_allocator<typename std::decay<StackAllocator>::type>::value>::type
412 {
413 spawn(ctx.get_executor(),
414 std::forward<Function>(function),
415 std::forward<StackAllocator>(salloc));
416 }
417
418 #endif // !defined(GENERATING_DOCUMENTATION)
419
420 } // namespace spawn