File indexing completed on 2025-01-18 09:28:52
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012 #ifndef BOOST_ASIO_EXPERIMENTAL_IMPL_CORO_HPP
0013 #define BOOST_ASIO_EXPERIMENTAL_IMPL_CORO_HPP
0014
0015 #if defined(_MSC_VER) && (_MSC_VER >= 1200)
0016 # pragma once
0017 #endif
0018
0019 #include <boost/asio/detail/config.hpp>
0020 #include <boost/asio/append.hpp>
0021 #include <boost/asio/associated_cancellation_slot.hpp>
0022 #include <boost/asio/bind_allocator.hpp>
0023 #include <boost/asio/deferred.hpp>
0024 #include <boost/asio/experimental/detail/coro_completion_handler.hpp>
0025 #include <boost/asio/detail/push_options.hpp>
0026
0027 namespace boost {
0028 namespace asio {
0029 namespace experimental {
0030
0031 template <typename Yield, typename Return,
0032 typename Executor, typename Allocator>
0033 struct coro;
0034
0035 namespace detail {
0036
0037 struct coro_cancellation_source
0038 {
0039 cancellation_slot slot;
0040 cancellation_state state;
0041 bool throw_if_cancelled_ = true;
0042
0043 void reset_cancellation_state()
0044 {
0045 state = cancellation_state(slot);
0046 }
0047
0048 template <typename Filter>
0049 void reset_cancellation_state(Filter&& filter)
0050 {
0051 state = cancellation_state(slot, static_cast<Filter&&>(filter));
0052 }
0053
0054 template <typename InFilter, typename OutFilter>
0055 void reset_cancellation_state(InFilter&& in_filter,
0056 OutFilter&& out_filter)
0057 {
0058 state = cancellation_state(slot,
0059 static_cast<InFilter&&>(in_filter),
0060 static_cast<OutFilter&&>(out_filter));
0061 }
0062
0063 bool throw_if_cancelled() const
0064 {
0065 return throw_if_cancelled_;
0066 }
0067
0068 void throw_if_cancelled(bool value)
0069 {
0070 throw_if_cancelled_ = value;
0071 }
0072 };
0073
0074 template <typename Signature, typename Return,
0075 typename Executor, typename Allocator>
0076 struct coro_promise;
0077
0078 template <typename T>
0079 struct is_noexcept : std::false_type
0080 {
0081 };
0082
0083 template <typename Return, typename... Args>
0084 struct is_noexcept<Return(Args...)> : std::false_type
0085 {
0086 };
0087
0088 template <typename Return, typename... Args>
0089 struct is_noexcept<Return(Args...) noexcept> : std::true_type
0090 {
0091 };
0092
0093 template <typename T>
0094 constexpr bool is_noexcept_v = is_noexcept<T>::value;
0095
0096 template <typename T>
0097 struct coro_error;
0098
0099 template <>
0100 struct coro_error<boost::system::error_code>
0101 {
0102 static boost::system::error_code invalid()
0103 {
0104 return boost::asio::error::fault;
0105 }
0106
0107 static boost::system::error_code cancelled()
0108 {
0109 return boost::asio::error::operation_aborted;
0110 }
0111
0112 static boost::system::error_code interrupted()
0113 {
0114 return boost::asio::error::interrupted;
0115 }
0116
0117 static boost::system::error_code done()
0118 {
0119 return boost::asio::error::broken_pipe;
0120 }
0121 };
0122
0123 template <>
0124 struct coro_error<std::exception_ptr>
0125 {
0126 static std::exception_ptr invalid()
0127 {
0128 return std::make_exception_ptr(
0129 boost::system::system_error(
0130 coro_error<boost::system::error_code>::invalid()));
0131 }
0132
0133 static std::exception_ptr cancelled()
0134 {
0135 return std::make_exception_ptr(
0136 boost::system::system_error(
0137 coro_error<boost::system::error_code>::cancelled()));
0138 }
0139
0140 static std::exception_ptr interrupted()
0141 {
0142 return std::make_exception_ptr(
0143 boost::system::system_error(
0144 coro_error<boost::system::error_code>::interrupted()));
0145 }
0146
0147 static std::exception_ptr done()
0148 {
0149 return std::make_exception_ptr(
0150 boost::system::system_error(
0151 coro_error<boost::system::error_code>::done()));
0152 }
0153 };
0154
0155 template <typename T, typename Coroutine >
0156 struct coro_with_arg
0157 {
0158 using coro_t = Coroutine;
0159 T value;
0160 coro_t& coro;
0161
0162 struct awaitable_t
0163 {
0164 T value;
0165 coro_t& coro;
0166
0167 constexpr static bool await_ready() { return false; }
0168
0169 template <typename Y, typename R, typename E, typename A>
0170 auto await_suspend(coroutine_handle<coro_promise<Y, R, E, A>> h)
0171 -> coroutine_handle<>
0172 {
0173 auto& hp = h.promise();
0174
0175 if constexpr (!coro_promise<Y, R, E, A>::is_noexcept)
0176 {
0177 if ((hp.cancel->state.cancelled() != cancellation_type::none)
0178 && hp.cancel->throw_if_cancelled_)
0179 {
0180 boost::asio::detail::throw_error(
0181 boost::asio::error::operation_aborted, "coro-cancelled");
0182 }
0183 }
0184
0185 if (hp.get_executor() == coro.get_executor())
0186 {
0187 coro.coro_->awaited_from = h;
0188 coro.coro_->reset_error();
0189 coro.coro_->input_ = std::move(value);
0190 coro.coro_->cancel = hp.cancel;
0191 return coro.coro_->get_handle();
0192 }
0193 else
0194 {
0195 coro.coro_->awaited_from =
0196 dispatch_coroutine(
0197 boost::asio::prefer(hp.get_executor(),
0198 execution::outstanding_work.tracked),
0199 [h]() mutable { h.resume(); }).handle;
0200
0201 coro.coro_->reset_error();
0202 coro.coro_->input_ = std::move(value);
0203
0204 struct cancel_handler
0205 {
0206 using src = std::pair<cancellation_signal,
0207 detail::coro_cancellation_source>;
0208
0209 std::shared_ptr<src> st = std::make_shared<src>();
0210
0211 cancel_handler(E e, coro_t& coro) : e(e), coro_(coro.coro_)
0212 {
0213 st->second.state =
0214 cancellation_state(st->second.slot = st->first.slot());
0215 }
0216
0217 E e;
0218 typename coro_t::promise_type* coro_;
0219
0220 void operator()(cancellation_type ct)
0221 {
0222 boost::asio::dispatch(e, [ct, st = st]() mutable
0223 {
0224 auto & [sig, state] = *st;
0225 sig.emit(ct);
0226 });
0227 }
0228 };
0229
0230 if (hp.cancel->state.slot().is_connected())
0231 {
0232 hp.cancel->state.slot().template emplace<cancel_handler>(
0233 coro.get_executor(), coro);
0234 }
0235
0236 auto hh = detail::coroutine_handle<
0237 typename coro_t::promise_type>::from_promise(*coro.coro_);
0238
0239 return dispatch_coroutine(
0240 coro.coro_->get_executor(), [hh]() mutable { hh.resume(); }).handle;
0241 }
0242 }
0243
0244 auto await_resume() -> typename coro_t::result_type
0245 {
0246 coro.coro_->cancel = nullptr;
0247 coro.coro_->rethrow_if();
0248 return std::move(coro.coro_->result_);
0249 }
0250 };
0251
0252 template <typename CompletionToken>
0253 auto async_resume(CompletionToken&& token) &&
0254 {
0255 return coro.async_resume(std::move(value),
0256 std::forward<CompletionToken>(token));
0257 }
0258
0259 auto operator co_await() &&
0260 {
0261 return awaitable_t{std::move(value), coro};
0262 }
0263 };
0264
0265 template <bool IsNoexcept>
0266 struct coro_promise_error;
0267
0268 template <>
0269 struct coro_promise_error<false>
0270 {
0271 std::exception_ptr error_;
0272
0273 void reset_error()
0274 {
0275 error_ = std::exception_ptr{};
0276 }
0277
0278 void unhandled_exception()
0279 {
0280 error_ = std::current_exception();
0281 }
0282
0283 void rethrow_if()
0284 {
0285 if (error_)
0286 std::rethrow_exception(error_);
0287 }
0288 };
0289
0290 #if defined(__GNUC__)
0291 # pragma GCC diagnostic push
0292 # if defined(__clang__)
0293 # pragma GCC diagnostic ignored "-Wexceptions"
0294 # else
0295 # pragma GCC diagnostic ignored "-Wterminate"
0296 # endif
0297 #elif defined(_MSC_VER)
0298 # pragma warning(push)
0299 # pragma warning (disable:4297)
0300 #endif
0301
0302 template <>
0303 struct coro_promise_error<true>
0304 {
0305 void reset_error()
0306 {
0307 }
0308
0309 void unhandled_exception() noexcept
0310 {
0311 throw;
0312 }
0313
0314 void rethrow_if()
0315 {
0316 }
0317 };
0318
0319 #if defined(__GNUC__)
0320 # pragma GCC diagnostic pop
0321 #elif defined(_MSC_VER)
0322 # pragma warning(pop)
0323 #endif
0324
0325 template <typename T = void>
0326 struct yield_input
0327 {
0328 T& value;
0329 coroutine_handle<> awaited_from{noop_coroutine()};
0330
0331 bool await_ready() const noexcept
0332 {
0333 return false;
0334 }
0335
0336 template <typename U>
0337 coroutine_handle<> await_suspend(coroutine_handle<U>) noexcept
0338 {
0339 return std::exchange(awaited_from, noop_coroutine());
0340 }
0341
0342 T await_resume() const noexcept
0343 {
0344 return std::move(value);
0345 }
0346 };
0347
0348 template <>
0349 struct yield_input<void>
0350 {
0351 coroutine_handle<> awaited_from{noop_coroutine()};
0352
0353 bool await_ready() const noexcept
0354 {
0355 return false;
0356 }
0357
0358 auto await_suspend(coroutine_handle<>) noexcept
0359 {
0360 return std::exchange(awaited_from, noop_coroutine());
0361 }
0362
0363 constexpr void await_resume() const noexcept
0364 {
0365 }
0366 };
0367
0368 struct coro_awaited_from
0369 {
0370 coroutine_handle<> awaited_from{noop_coroutine()};
0371
0372 auto final_suspend() noexcept
0373 {
0374 struct suspendor
0375 {
0376 coroutine_handle<> awaited_from;
0377
0378 constexpr static bool await_ready() noexcept
0379 {
0380 return false;
0381 }
0382
0383 auto await_suspend(coroutine_handle<>) noexcept
0384 {
0385 return std::exchange(awaited_from, noop_coroutine());
0386 }
0387
0388 constexpr static void await_resume() noexcept
0389 {
0390 }
0391 };
0392
0393 return suspendor{std::exchange(awaited_from, noop_coroutine())};
0394 }
0395
0396 ~coro_awaited_from()
0397 {
0398 awaited_from.resume();
0399 }
0400 };
0401
0402 template <typename Yield, typename Input, typename Return>
0403 struct coro_promise_exchange : coro_awaited_from
0404 {
0405 using result_type = coro_result_t<Yield, Return>;
0406
0407 result_type result_;
0408 Input input_;
0409
0410 auto yield_value(Yield&& y)
0411 {
0412 result_ = std::move(y);
0413 return yield_input<Input>{std::move(input_),
0414 std::exchange(awaited_from, noop_coroutine())};
0415 }
0416
0417 auto yield_value(const Yield& y)
0418 {
0419 result_ = y;
0420 return yield_input<Input>{std::move(input_),
0421 std::exchange(awaited_from, noop_coroutine())};
0422 }
0423
0424 void return_value(const Return& r)
0425 {
0426 result_ = r;
0427 }
0428
0429 void return_value(Return&& r)
0430 {
0431 result_ = std::move(r);
0432 }
0433 };
0434
0435 template <typename YieldReturn>
0436 struct coro_promise_exchange<YieldReturn, void, YieldReturn> : coro_awaited_from
0437 {
0438 using result_type = coro_result_t<YieldReturn, YieldReturn>;
0439
0440 result_type result_;
0441
0442 auto yield_value(const YieldReturn& y)
0443 {
0444 result_ = y;
0445 return yield_input<void>{std::exchange(awaited_from, noop_coroutine())};
0446 }
0447
0448 auto yield_value(YieldReturn&& y)
0449 {
0450 result_ = std::move(y);
0451 return yield_input<void>{std::exchange(awaited_from, noop_coroutine())};
0452 }
0453
0454 void return_value(const YieldReturn& r)
0455 {
0456 result_ = r;
0457 }
0458
0459 void return_value(YieldReturn&& r)
0460 {
0461 result_ = std::move(r);
0462 }
0463 };
0464
0465 template <typename Yield, typename Return>
0466 struct coro_promise_exchange<Yield, void, Return> : coro_awaited_from
0467 {
0468 using result_type = coro_result_t<Yield, Return>;
0469
0470 result_type result_;
0471
0472 auto yield_value(const Yield& y)
0473 {
0474 result_.template emplace<0>(y);
0475 return yield_input<void>{std::exchange(awaited_from, noop_coroutine())};
0476 }
0477
0478 auto yield_value(Yield&& y)
0479 {
0480 result_.template emplace<0>(std::move(y));
0481 return yield_input<void>{std::exchange(awaited_from, noop_coroutine())};
0482 }
0483
0484 void return_value(const Return& r)
0485 {
0486 result_.template emplace<1>(r);
0487 }
0488
0489 void return_value(Return&& r)
0490 {
0491 result_.template emplace<1>(std::move(r));
0492 }
0493 };
0494
0495 template <typename Yield, typename Input>
0496 struct coro_promise_exchange<Yield, Input, void> : coro_awaited_from
0497 {
0498 using result_type = coro_result_t<Yield, void>;
0499
0500 result_type result_;
0501 Input input_;
0502
0503 auto yield_value(Yield&& y)
0504 {
0505 result_ = std::move(y);
0506 return yield_input<Input>{input_,
0507 std::exchange(awaited_from, noop_coroutine())};
0508 }
0509
0510 auto yield_value(const Yield& y)
0511 {
0512 result_ = y;
0513 return yield_input<Input>{input_,
0514 std::exchange(awaited_from, noop_coroutine())};
0515 }
0516
0517 void return_void()
0518 {
0519 result_.reset();
0520 }
0521 };
0522
0523 template <typename Return>
0524 struct coro_promise_exchange<void, void, Return> : coro_awaited_from
0525 {
0526 using result_type = coro_result_t<void, Return>;
0527
0528 result_type result_;
0529
0530 void yield_value();
0531
0532 void return_value(const Return& r)
0533 {
0534 result_ = r;
0535 }
0536
0537 void return_value(Return&& r)
0538 {
0539 result_ = std::move(r);
0540 }
0541 };
0542
0543 template <>
0544 struct coro_promise_exchange<void, void, void> : coro_awaited_from
0545 {
0546 void return_void() {}
0547
0548 void yield_value();
0549 };
0550
0551 template <typename Yield>
0552 struct coro_promise_exchange<Yield, void, void> : coro_awaited_from
0553 {
0554 using result_type = coro_result_t<Yield, void>;
0555
0556 result_type result_;
0557
0558 auto yield_value(const Yield& y)
0559 {
0560 result_ = y;
0561 return yield_input<void>{std::exchange(awaited_from, noop_coroutine())};
0562 }
0563
0564 auto yield_value(Yield&& y)
0565 {
0566 result_ = std::move(y);
0567 return yield_input<void>{std::exchange(awaited_from, noop_coroutine())};
0568 }
0569
0570 void return_void()
0571 {
0572 result_.reset();
0573 }
0574 };
0575
0576 template <typename Yield, typename Return,
0577 typename Executor, typename Allocator>
0578 struct coro_promise final :
0579 coro_promise_allocator<Allocator>,
0580 coro_promise_error<coro_traits<Yield, Return, Executor>::is_noexcept>,
0581 coro_promise_exchange<
0582 typename coro_traits<Yield, Return, Executor>::yield_type,
0583 typename coro_traits<Yield, Return, Executor>::input_type,
0584 typename coro_traits<Yield, Return, Executor>::return_type>
0585 {
0586 using coro_type = coro<Yield, Return, Executor, Allocator>;
0587
0588 auto handle()
0589 {
0590 return coroutine_handle<coro_promise>::from_promise(this);
0591 }
0592
0593 using executor_type = Executor;
0594
0595 executor_type executor_;
0596
0597 std::optional<coro_cancellation_source> cancel_source;
0598 coro_cancellation_source * cancel;
0599
0600 using cancellation_slot_type = boost::asio::cancellation_slot;
0601
0602 cancellation_slot_type get_cancellation_slot() const noexcept
0603 {
0604 return cancel ? cancel->slot : cancellation_slot_type{};
0605 }
0606
0607 using allocator_type =
0608 typename std::allocator_traits<associated_allocator_t<Executor>>::
0609 template rebind_alloc<std::byte>;
0610 using traits = coro_traits<Yield, Return, Executor>;
0611
0612 using input_type = typename traits::input_type;
0613 using yield_type = typename traits::yield_type;
0614 using return_type = typename traits::return_type;
0615 using error_type = typename traits::error_type;
0616 using result_type = typename traits::result_type;
0617 constexpr static bool is_noexcept = traits::is_noexcept;
0618
0619 auto get_executor() const -> Executor
0620 {
0621 return executor_;
0622 }
0623
0624 auto get_handle()
0625 {
0626 return coroutine_handle<coro_promise>::from_promise(*this);
0627 }
0628
0629 template <typename... Args>
0630 coro_promise(Executor executor, Args&&... args) noexcept
0631 : coro_promise_allocator<Allocator>(
0632 executor, std::forward<Args>(args)...),
0633 executor_(std::move(executor))
0634 {
0635 }
0636
0637 template <typename First, typename... Args>
0638 coro_promise(First&& f, Executor executor, Args&&... args) noexcept
0639 : coro_promise_allocator<Allocator>(
0640 f, executor, std::forward<Args>(args)...),
0641 executor_(std::move(executor))
0642 {
0643 }
0644
0645 template <typename First, detail::execution_context Context, typename... Args>
0646 coro_promise(First&& f, Context&& ctx, Args&&... args) noexcept
0647 : coro_promise_allocator<Allocator>(
0648 f, ctx, std::forward<Args>(args)...),
0649 executor_(ctx.get_executor())
0650 {
0651 }
0652
0653 template <detail::execution_context Context, typename... Args>
0654 coro_promise(Context&& ctx, Args&&... args) noexcept
0655 : coro_promise_allocator<Allocator>(
0656 ctx, std::forward<Args>(args)...),
0657 executor_(ctx.get_executor())
0658 {
0659 }
0660
0661 auto get_return_object()
0662 {
0663 return coro<Yield, Return, Executor, Allocator>{this};
0664 }
0665
0666 auto initial_suspend() noexcept
0667 {
0668 return suspend_always{};
0669 }
0670
0671 using coro_promise_exchange<
0672 typename coro_traits<Yield, Return, Executor>::yield_type,
0673 typename coro_traits<Yield, Return, Executor>::input_type,
0674 typename coro_traits<Yield, Return, Executor>::return_type>::yield_value;
0675
0676 auto await_transform(this_coro::executor_t) const
0677 {
0678 struct exec_helper
0679 {
0680 const executor_type& value;
0681
0682 constexpr static bool await_ready() noexcept
0683 {
0684 return true;
0685 }
0686
0687 constexpr static void await_suspend(coroutine_handle<>) noexcept
0688 {
0689 }
0690
0691 executor_type await_resume() const noexcept
0692 {
0693 return value;
0694 }
0695 };
0696
0697 return exec_helper{executor_};
0698 }
0699
0700 auto await_transform(this_coro::cancellation_state_t) const
0701 {
0702 struct exec_helper
0703 {
0704 const boost::asio::cancellation_state& value;
0705
0706 constexpr static bool await_ready() noexcept
0707 {
0708 return true;
0709 }
0710
0711 constexpr static void await_suspend(coroutine_handle<>) noexcept
0712 {
0713 }
0714
0715 boost::asio::cancellation_state await_resume() const noexcept
0716 {
0717 return value;
0718 }
0719 };
0720 assert(cancel);
0721 return exec_helper{cancel->state};
0722 }
0723
0724
0725 auto await_transform(this_coro::reset_cancellation_state_0_t) noexcept
0726 {
0727 struct result
0728 {
0729 detail::coro_cancellation_source * src_;
0730
0731 bool await_ready() const noexcept
0732 {
0733 return true;
0734 }
0735
0736 void await_suspend(coroutine_handle<void>) noexcept
0737 {
0738 }
0739
0740 auto await_resume() const
0741 {
0742 return src_->reset_cancellation_state();
0743 }
0744 };
0745
0746 return result{cancel};
0747 }
0748
0749
0750 template <typename Filter>
0751 auto await_transform(
0752 this_coro::reset_cancellation_state_1_t<Filter> reset) noexcept
0753 {
0754 struct result
0755 {
0756 detail::coro_cancellation_source* src_;
0757 Filter filter_;
0758
0759 bool await_ready() const noexcept
0760 {
0761 return true;
0762 }
0763
0764 void await_suspend(coroutine_handle<void>) noexcept
0765 {
0766 }
0767
0768 auto await_resume()
0769 {
0770 return src_->reset_cancellation_state(
0771 static_cast<Filter&&>(filter_));
0772 }
0773 };
0774
0775 return result{cancel, static_cast<Filter&&>(reset.filter)};
0776 }
0777
0778
0779 template <typename InFilter, typename OutFilter>
0780 auto await_transform(
0781 this_coro::reset_cancellation_state_2_t<InFilter, OutFilter> reset)
0782 noexcept
0783 {
0784 struct result
0785 {
0786 detail::coro_cancellation_source* src_;
0787 InFilter in_filter_;
0788 OutFilter out_filter_;
0789
0790 bool await_ready() const noexcept
0791 {
0792 return true;
0793 }
0794
0795 void await_suspend(coroutine_handle<void>) noexcept
0796 {
0797 }
0798
0799 auto await_resume()
0800 {
0801 return src_->reset_cancellation_state(
0802 static_cast<InFilter&&>(in_filter_),
0803 static_cast<OutFilter&&>(out_filter_));
0804 }
0805 };
0806
0807 return result{cancel,
0808 static_cast<InFilter&&>(reset.in_filter),
0809 static_cast<OutFilter&&>(reset.out_filter)};
0810 }
0811
0812
0813
0814 auto await_transform(this_coro::throw_if_cancelled_0_t) noexcept
0815 requires (!is_noexcept)
0816 {
0817 struct result
0818 {
0819 detail::coro_cancellation_source* src_;
0820
0821 bool await_ready() const noexcept
0822 {
0823 return true;
0824 }
0825
0826 void await_suspend(coroutine_handle<void>) noexcept
0827 {
0828 }
0829
0830 auto await_resume()
0831 {
0832 return src_->throw_if_cancelled();
0833 }
0834 };
0835
0836 return result{cancel};
0837 }
0838
0839
0840
0841 auto await_transform(
0842 this_coro::throw_if_cancelled_1_t throw_if_cancelled) noexcept
0843 requires (!is_noexcept)
0844 {
0845 struct result
0846 {
0847 detail::coro_cancellation_source* src_;
0848 bool value_;
0849
0850 bool await_ready() const noexcept
0851 {
0852 return true;
0853 }
0854
0855 void await_suspend(coroutine_handle<void>) noexcept
0856 {
0857 }
0858
0859 auto await_resume()
0860 {
0861 src_->throw_if_cancelled(value_);
0862 }
0863 };
0864
0865 return result{cancel, throw_if_cancelled.value};
0866 }
0867
0868 template <typename Yield_, typename Return_,
0869 typename Executor_, typename Allocator_>
0870 auto await_transform(coro<Yield_, Return_, Executor_, Allocator_>& kr)
0871 -> decltype(auto)
0872 {
0873 return kr;
0874 }
0875
0876 template <typename Yield_, typename Return_,
0877 typename Executor_, typename Allocator_>
0878 auto await_transform(coro<Yield_, Return_, Executor_, Allocator_>&& kr)
0879 {
0880 return std::move(kr);
0881 }
0882
0883 template <typename T_, typename Coroutine >
0884 auto await_transform(coro_with_arg<T_, Coroutine>&& kr) -> decltype(auto)
0885 {
0886 return std::move(kr);
0887 }
0888
0889 template <typename T_>
0890 requires requires(T_ t) {{ t.async_wait(deferred) }; }
0891 auto await_transform(T_& t) -> decltype(auto)
0892 {
0893 return await_transform(t.async_wait(deferred));
0894 }
0895
0896 template <typename Op>
0897 auto await_transform(Op&& op,
0898 constraint_t<is_async_operation<Op>::value> = 0)
0899 {
0900 if ((cancel->state.cancelled() != cancellation_type::none)
0901 && cancel->throw_if_cancelled_)
0902 {
0903 boost::asio::detail::throw_error(
0904 boost::asio::error::operation_aborted, "coro-cancelled");
0905 }
0906 using signature = completion_signature_of_t<Op>;
0907 using result_type = detail::coro_completion_handler_type_t<signature>;
0908 using handler_type =
0909 typename detail::coro_completion_handler_type<signature>::template
0910 completion_handler<coro_promise>;
0911
0912 struct aw_t
0913 {
0914 Op op;
0915 std::optional<result_type> result;
0916
0917 constexpr static bool await_ready()
0918 {
0919 return false;
0920 }
0921
0922 void await_suspend(coroutine_handle<coro_promise> h)
0923 {
0924 std::move(op)(handler_type{h, result});
0925 }
0926
0927 auto await_resume()
0928 {
0929 if constexpr (is_noexcept)
0930 {
0931 if constexpr (std::tuple_size_v<result_type> == 0u)
0932 return;
0933 else if constexpr (std::tuple_size_v<result_type> == 1u)
0934 return std::get<0>(std::move(result).value());
0935 else
0936 return std::move(result).value();
0937 }
0938 else
0939 return detail::coro_interpret_result(std::move(result).value());
0940 }
0941 };
0942
0943 return aw_t{std::move(op), {}};
0944 }
0945 };
0946
0947 }
0948
0949 template <typename Yield, typename Return,
0950 typename Executor, typename Allocator>
0951 struct coro<Yield, Return, Executor, Allocator>::awaitable_t
0952 {
0953 coro& coro_;
0954
0955 constexpr static bool await_ready() { return false; }
0956
0957 template <typename Y, typename R, typename E, typename A>
0958 auto await_suspend(
0959 detail::coroutine_handle<detail::coro_promise<Y, R, E, A>> h)
0960 -> detail::coroutine_handle<>
0961 {
0962 auto& hp = h.promise();
0963
0964 if constexpr (!detail::coro_promise<Y, R, E, A>::is_noexcept)
0965 {
0966 if ((hp.cancel->state.cancelled() != cancellation_type::none)
0967 && hp.cancel->throw_if_cancelled_)
0968 {
0969 boost::asio::detail::throw_error(
0970 boost::asio::error::operation_aborted, "coro-cancelled");
0971 }
0972 }
0973
0974 if (hp.get_executor() == coro_.get_executor())
0975 {
0976 coro_.coro_->awaited_from = h;
0977 coro_.coro_->cancel = hp.cancel;
0978 coro_.coro_->reset_error();
0979
0980 return coro_.coro_->get_handle();
0981 }
0982 else
0983 {
0984 coro_.coro_->awaited_from = detail::dispatch_coroutine(
0985 boost::asio::prefer(hp.get_executor(),
0986 execution::outstanding_work.tracked),
0987 [h]() mutable
0988 {
0989 h.resume();
0990 }).handle;
0991
0992 coro_.coro_->reset_error();
0993
0994 struct cancel_handler
0995 {
0996 std::shared_ptr<std::pair<cancellation_signal,
0997 detail::coro_cancellation_source>> st = std::make_shared<
0998 std::pair<cancellation_signal, detail::coro_cancellation_source>>();
0999
1000 cancel_handler(E e, coro& coro) : e(e), coro_(coro.coro_)
1001 {
1002 st->second.state = cancellation_state(
1003 st->second.slot = st->first.slot());
1004 }
1005
1006 E e;
1007 typename coro::promise_type* coro_;
1008
1009 void operator()(cancellation_type ct)
1010 {
1011 boost::asio::dispatch(e,
1012 [ct, st = st]() mutable
1013 {
1014 auto & [sig, state] = *st;
1015 sig.emit(ct);
1016 });
1017 }
1018 };
1019
1020 if (hp.cancel->state.slot().is_connected())
1021 {
1022 hp.cancel->state.slot().template emplace<cancel_handler>(
1023 coro_.get_executor(), coro_);
1024 }
1025
1026 auto hh = detail::coroutine_handle<
1027 detail::coro_promise<Yield, Return, Executor, Allocator>>::from_promise(
1028 *coro_.coro_);
1029
1030 return detail::dispatch_coroutine(
1031 coro_.coro_->get_executor(),
1032 [hh]() mutable { hh.resume(); }).handle;
1033 }
1034 }
1035
1036 auto await_resume() -> result_type
1037 {
1038 coro_.coro_->cancel = nullptr;
1039 coro_.coro_->rethrow_if();
1040 if constexpr (!std::is_void_v<result_type>)
1041 return std::move(coro_.coro_->result_);
1042 }
1043 };
1044
1045 template <typename Yield, typename Return,
1046 typename Executor, typename Allocator>
1047 struct coro<Yield, Return, Executor, Allocator>::initiate_async_resume
1048 {
1049 typedef Executor executor_type;
1050 typedef Allocator allocator_type;
1051 typedef boost::asio::cancellation_slot cancellation_slot_type;
1052
1053 explicit initiate_async_resume(coro* self)
1054 : coro_(self->coro_)
1055 {
1056 }
1057
1058 executor_type get_executor() const noexcept
1059 {
1060 return coro_->get_executor();
1061 }
1062
1063 allocator_type get_allocator() const noexcept
1064 {
1065 return coro_->get_allocator();
1066 }
1067
1068 template <typename E, typename WaitHandler>
1069 auto handle(E exec, WaitHandler&& handler,
1070 std::true_type ,
1071 std::true_type )
1072 {
1073 return [this, the_coro = coro_,
1074 h = std::forward<WaitHandler>(handler),
1075 exec = std::move(exec)]() mutable
1076 {
1077 assert(the_coro);
1078
1079 auto ch = detail::coroutine_handle<promise_type>::from_promise(*the_coro);
1080 assert(ch && !ch.done());
1081
1082 the_coro->awaited_from = post_coroutine(std::move(exec), std::move(h));
1083 the_coro->reset_error();
1084 ch.resume();
1085 };
1086 }
1087
1088 template <typename E, typename WaitHandler>
1089 requires (!std::is_void_v<result_type>)
1090 auto handle(E exec, WaitHandler&& handler,
1091 std::true_type ,
1092 std::false_type )
1093 {
1094 return [the_coro = coro_,
1095 h = std::forward<WaitHandler>(handler),
1096 exec = std::move(exec)]() mutable
1097 {
1098 assert(the_coro);
1099
1100 auto ch = detail::coroutine_handle<promise_type>::from_promise(*the_coro);
1101 assert(ch && !ch.done());
1102
1103 the_coro->awaited_from = detail::post_coroutine(
1104 exec, std::move(h), the_coro->result_).handle;
1105 the_coro->reset_error();
1106 ch.resume();
1107 };
1108 }
1109
1110 template <typename E, typename WaitHandler>
1111 auto handle(E exec, WaitHandler&& handler,
1112 std::false_type ,
1113 std::true_type )
1114 {
1115 return [the_coro = coro_,
1116 h = std::forward<WaitHandler>(handler),
1117 exec = std::move(exec)]() mutable
1118 {
1119 if (!the_coro)
1120 return boost::asio::post(exec,
1121 boost::asio::append(std::move(h),
1122 detail::coro_error<error_type>::invalid()));
1123
1124 auto ch = detail::coroutine_handle<promise_type>::from_promise(*the_coro);
1125 if (!ch)
1126 return boost::asio::post(exec,
1127 boost::asio::append(std::move(h),
1128 detail::coro_error<error_type>::invalid()));
1129 else if (ch.done())
1130 return boost::asio::post(exec,
1131 boost::asio::append(std::move(h),
1132 detail::coro_error<error_type>::done()));
1133 else
1134 {
1135 the_coro->awaited_from = detail::post_coroutine(
1136 exec, std::move(h), the_coro->error_).handle;
1137 the_coro->reset_error();
1138 ch.resume();
1139 }
1140 };
1141 }
1142
1143 template <typename E, typename WaitHandler>
1144 auto handle(E exec, WaitHandler&& handler,
1145 std::false_type ,
1146 std::false_type )
1147 {
1148 return [the_coro = coro_,
1149 h = std::forward<WaitHandler>(handler),
1150 exec = std::move(exec)]() mutable
1151 {
1152 if (!the_coro)
1153 return boost::asio::post(exec,
1154 boost::asio::append(std::move(h),
1155 detail::coro_error<error_type>::invalid(), result_type{}));
1156
1157 auto ch =
1158 detail::coroutine_handle<promise_type>::from_promise(*the_coro);
1159 if (!ch)
1160 return boost::asio::post(exec,
1161 boost::asio::append(std::move(h),
1162 detail::coro_error<error_type>::invalid(), result_type{}));
1163 else if (ch.done())
1164 return boost::asio::post(exec,
1165 boost::asio::append(std::move(h),
1166 detail::coro_error<error_type>::done(), result_type{}));
1167 else
1168 {
1169 the_coro->awaited_from = detail::post_coroutine(
1170 exec, std::move(h), the_coro->error_, the_coro->result_).handle;
1171 the_coro->reset_error();
1172 ch.resume();
1173 }
1174 };
1175 }
1176
1177 template <typename WaitHandler>
1178 void operator()(WaitHandler&& handler)
1179 {
1180 const auto exec = boost::asio::prefer(
1181 get_associated_executor(handler, get_executor()),
1182 execution::outstanding_work.tracked);
1183
1184 coro_->cancel = &coro_->cancel_source.emplace();
1185 coro_->cancel->state = cancellation_state(
1186 coro_->cancel->slot = get_associated_cancellation_slot(handler));
1187 boost::asio::dispatch(get_executor(),
1188 handle(exec, std::forward<WaitHandler>(handler),
1189 std::integral_constant<bool, is_noexcept>{},
1190 std::is_void<result_type>{}));
1191 }
1192
1193 template <typename WaitHandler, typename Input>
1194 void operator()(WaitHandler&& handler, Input&& input)
1195 {
1196 const auto exec = boost::asio::prefer(
1197 get_associated_executor(handler, get_executor()),
1198 execution::outstanding_work.tracked);
1199
1200 coro_->cancel = &coro_->cancel_source.emplace();
1201 coro_->cancel->state = cancellation_state(
1202 coro_->cancel->slot = get_associated_cancellation_slot(handler));
1203 boost::asio::dispatch(get_executor(),
1204 [h = handle(exec, std::forward<WaitHandler>(handler),
1205 std::integral_constant<bool, is_noexcept>{},
1206 std::is_void<result_type>{}),
1207 in = std::forward<Input>(input), the_coro = coro_]() mutable
1208 {
1209 the_coro->input_ = std::move(in);
1210 std::move(h)();
1211 });
1212 }
1213
1214 private:
1215 typename coro::promise_type* coro_;
1216 };
1217
1218 }
1219 }
1220 }
1221
1222 #include <boost/asio/detail/pop_options.hpp>
1223
1224 #endif