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)
18 #include <boost/system/system_error.hpp>
19 #include <boost/context/continuation.hpp>
21 #include <spawn/detail/net.hpp>
22 #include <spawn/detail/is_stack_allocator.hpp>
24 namespace spawn::detail {
26 class continuation_context
29 boost::context::continuation context_;
31 continuation_context()
38 context_ = context_.resume();
42 template <typename Handler, typename T>
46 coro_handler(basic_yield_context<Handler> ctx)
47 : callee_(ctx.callee_.lock()),
49 handler_(ctx.handler_),
56 void operator()(T value)
58 *ec_ = boost::system::error_code();
59 *value_ = std::move(value);
64 void operator()(boost::system::error_code ec, T value)
67 *value_ = std::move(value);
73 std::shared_ptr<continuation_context> callee_;
74 continuation_context& caller_;
76 std::atomic<long>* ready_;
77 boost::system::error_code* ec_;
81 template <typename Handler>
82 class coro_handler<Handler, void>
85 coro_handler(basic_yield_context<Handler> ctx)
86 : callee_(ctx.callee_.lock()),
88 handler_(ctx.handler_),
96 *ec_ = boost::system::error_code();
101 void operator()(boost::system::error_code ec)
109 std::shared_ptr<continuation_context> callee_;
110 continuation_context& caller_;
112 std::atomic<long>* ready_;
113 boost::system::error_code* ec_;
116 template <typename Handler, typename T>
117 class coro_async_result
120 using completion_handler_type = coro_handler<Handler, T>;
121 using return_type = T;
123 explicit coro_async_result(completion_handler_type& h)
130 if (!out_ec_) h.ec_ = &ec_;
136 // Must not hold shared_ptr while suspended.
137 handler_.callee_.reset();
140 caller_.resume(); // suspend caller
141 if (!out_ec_ && ec_) throw boost::system::system_error(ec_);
142 return std::move(value_);
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_;
154 template <typename Handler>
155 class coro_async_result<Handler, void>
158 using completion_handler_type = coro_handler<Handler, void>;
159 using return_type = void;
161 explicit coro_async_result(completion_handler_type& h)
168 if (!out_ec_) h.ec_ = &ec_;
173 // Must not hold shared_ptr while suspended.
174 handler_.callee_.reset();
177 caller_.resume(); // suspend caller
178 if (!out_ec_ && ec_) throw boost::system::system_error(ec_);
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_;
189 } // namespace spawn::detail
191 #if !defined(GENERATING_DOCUMENTATION)
192 namespace SPAWN_NET_NAMESPACE {
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>
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)
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>
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)
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>
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)
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>
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)
248 template <typename Handler, typename T, typename Allocator>
249 struct associated_allocator<spawn::detail::coro_handler<Handler, T>, Allocator>
251 using type = associated_allocator_t<Handler, Allocator>;
253 static type get(const spawn::detail::coro_handler<Handler, T>& h,
254 const Allocator& a = Allocator()) noexcept
256 return associated_allocator<Handler, Allocator>::get(h.handler_, a);
260 template <typename Handler, typename T, typename Executor>
261 struct associated_executor<spawn::detail::coro_handler<Handler, T>, Executor>
263 using type = associated_executor_t<Handler, Executor>;
265 static type get(const spawn::detail::coro_handler<Handler, T>& h,
266 const Executor& ex = Executor()) noexcept
268 return associated_executor<Handler, Executor>::get(h.handler_, ex);
272 } // namespace SPAWN_NET_NAMESPACE
277 template <typename Handler, typename Function, typename StackAllocator>
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))
288 spawn_data(const spawn_data&) = delete;
289 spawn_data& operator=(const spawn_data&) = delete;
294 StackAllocator salloc_;
295 continuation_context caller_;
298 template <typename Handler, typename Function, typename StackAllocator>
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)
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_)
316 boost::context::continuation caller = std::move(data->caller_.context_);
322 std::shared_ptr<continuation_context> callee_;
323 std::shared_ptr<spawn_data<Handler, Function, StackAllocator> > data_;
326 inline void default_spawn_handler() {}
328 } // namespace detail
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
335 auto ex = detail::net::get_associated_executor(function);
337 spawn(ex, std::forward<Function>(function), salloc);
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
347 using handler_type = typename std::decay<Handler>::type;
348 using function_type = typename std::decay<Function>::type;
350 auto ex = detail::net::get_associated_executor(handler);
351 auto a = detail::net::get_associated_allocator(handler);
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));
360 ex.dispatch(helper, a);
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
369 using function_type = typename std::decay<Function>::type;
371 Handler handler(ctx.handler_); // Explicit copy that might be moved from.
373 auto ex = detail::net::get_associated_executor(handler);
374 auto a = detail::net::get_associated_allocator(handler);
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));
383 ex.dispatch(helper, a);
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
391 spawn(detail::net::strand<Executor>(ex),
392 std::forward<Function>(function),
393 std::forward<StackAllocator>(salloc));
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
402 spawn(bind_executor(ex, &detail::default_spawn_handler),
403 std::forward<Function>(function),
404 std::forward<StackAllocator>(salloc));
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
413 spawn(ctx.get_executor(),
414 std::forward<Function>(function),
415 std::forward<StackAllocator>(salloc));
418 #endif // !defined(GENERATING_DOCUMENTATION)