File indexing completed on 2025-12-16 09:44:24
0001
0002
0003
0004
0005
0006
0007
0008 #ifndef BOOST_COBALT_DETAIL_GENERATOR_HPP
0009 #define BOOST_COBALT_DETAIL_GENERATOR_HPP
0010
0011 #include <boost/cobalt/concepts.hpp>
0012 #include <boost/cobalt/result.hpp>
0013 #include <boost/cobalt/detail/exception.hpp>
0014 #include <boost/cobalt/detail/forward_cancellation.hpp>
0015 #include <boost/cobalt/detail/this_thread.hpp>
0016 #include <boost/cobalt/unique_handle.hpp>
0017 #include <boost/cobalt/detail/wrapper.hpp>
0018 #include <boost/cobalt/op.hpp>
0019 #include <boost/cobalt/noop.hpp>
0020
0021 #include <boost/asio/bind_allocator.hpp>
0022 #include <boost/core/exchange.hpp>
0023 #include <boost/variant2/variant.hpp>
0024
0025 namespace boost::cobalt
0026 {
0027
0028
0029 template<typename Yield, typename Push>
0030 struct generator;
0031
0032 namespace detail
0033 {
0034
0035 template<typename Yield, typename Push>
0036 struct generator_yield_awaitable;
0037
0038 template<typename Yield, typename Push>
0039 struct generator_receiver;
0040
0041 template<typename Yield, typename Push>
0042 struct generator_receiver_base
0043 {
0044 std::optional<Push> pushed_value;
0045 auto get_awaitable(const Push & push) requires std::is_copy_constructible_v<Push>
0046 {
0047 using impl = generator_receiver<Yield, Push>;
0048 return typename impl::awaitable{static_cast<impl*>(this), &push};
0049 }
0050 auto get_awaitable( Push && push)
0051 {
0052 using impl = generator_receiver<Yield, Push>;
0053 return typename impl::awaitable{static_cast<impl*>(this), &push};
0054 }
0055 };
0056
0057 template<typename Yield>
0058 struct generator_receiver_base<Yield, void>
0059 {
0060 bool pushed_value{false};
0061
0062 auto get_awaitable()
0063 {
0064 using impl = generator_receiver<Yield, void>;
0065 return typename impl::awaitable{static_cast<impl*>(this), static_cast<void*>(nullptr)};
0066 }
0067 };
0068
0069 template<typename Yield, typename Push>
0070 struct generator_promise;
0071
0072 template<typename Yield, typename Push>
0073 struct generator_receiver : generator_receiver_base<Yield, Push>
0074 {
0075 std::exception_ptr exception;
0076 std::optional<Yield> result, result_buffer;
0077 Yield get_result()
0078 {
0079 if (result_buffer)
0080 {
0081 auto res = *std::exchange(result, std::nullopt);
0082 if (result_buffer)
0083 result.emplace(*std::exchange(result_buffer, std::nullopt));
0084 return res;
0085 }
0086 else
0087 return *std::exchange(result, std::nullopt);
0088 }
0089 bool done = false;
0090 unique_handle<void> awaited_from{nullptr};
0091 unique_handle<generator_promise<Yield, Push>> yield_from{nullptr};
0092
0093 bool lazy = false;
0094
0095 bool ready() { return exception || result || done; }
0096
0097 generator_receiver(noop<Yield> n) : result(std::move(n.value)), done(true) {}
0098
0099 generator_receiver() = default;
0100 generator_receiver(generator_receiver && lhs)
0101 : generator_receiver_base<Yield, Push>{std::move(lhs.pushed_value)},
0102 exception(std::move(lhs.exception)),
0103 result(std::move(lhs.result)),
0104 result_buffer(std::move(lhs.result_buffer)), done(lhs.done),
0105 awaited_from(std::move(lhs.awaited_from)), yield_from{std::move(lhs.yield_from)},
0106 lazy(lhs.lazy), reference(lhs.reference), cancel_signal(lhs.cancel_signal)
0107
0108 {
0109 if (!lhs.done && !lhs.exception)
0110 {
0111 *reference = this;
0112 lhs.exception = moved_from_exception();
0113 }
0114 lhs.done = true;
0115 }
0116
0117 ~generator_receiver()
0118 {
0119 if (!done && *reference == this)
0120 *reference = nullptr;
0121 }
0122
0123 generator_receiver(generator_receiver * &reference, asio::cancellation_signal & cancel_signal)
0124 : reference(&reference), cancel_signal(&cancel_signal)
0125 {
0126 reference = this;
0127 }
0128
0129
0130 generator_receiver& operator=(generator_receiver && lhs) noexcept
0131 {
0132 if (*reference == this)
0133 {
0134 *reference = nullptr;
0135 }
0136
0137 generator_receiver_base<Yield, Push>::operator=(std::move(lhs));
0138 exception = std::move(lhs.exception);
0139 done = lhs.done;
0140 result = std::move(lhs.result);
0141 result_buffer = std::move(lhs.result_buffer);
0142 awaited_from = std::move(lhs.awaited_from);
0143 yield_from = std::move(lhs.yield_from);
0144 lazy = lhs.lazy;
0145 reference = lhs.reference;
0146 cancel_signal = lhs.cancel_signal;
0147
0148 if (!lhs.done && !lhs.exception)
0149 {
0150 *reference = this;
0151 lhs.exception = moved_from_exception();
0152 }
0153 lhs.done = true;
0154
0155 return *this;
0156 }
0157
0158 generator_receiver **reference;
0159 asio::cancellation_signal * cancel_signal;
0160
0161 using yield_awaitable = generator_yield_awaitable<Yield, Push>;
0162
0163 yield_awaitable get_yield_awaitable(generator_promise<Yield, Push> * pro) {return {pro}; }
0164 static yield_awaitable terminator() {return {nullptr}; }
0165
0166 template<typename T>
0167 void yield_value(T && t)
0168 {
0169 if (!result)
0170 result.emplace(std::forward<T>(t));
0171 else
0172 {
0173 BOOST_ASSERT(!result_buffer);
0174 result_buffer.emplace(std::forward<T>(t));
0175 }
0176
0177 }
0178
0179 struct awaitable
0180 {
0181 generator_receiver *self;
0182 std::exception_ptr ex;
0183 asio::cancellation_slot cl;
0184
0185 variant2::variant<variant2::monostate, Push *, const Push *> to_push;
0186
0187
0188 awaitable(generator_receiver * self, Push * to_push) : self(self), to_push(to_push)
0189 {
0190 }
0191 awaitable(generator_receiver * self, const Push * to_push)
0192 : self(self), to_push(to_push)
0193 {
0194 }
0195
0196 awaitable(const awaitable & aw) noexcept : self(aw.self), to_push(aw.to_push)
0197 {
0198 }
0199
0200 bool await_ready() const
0201 {
0202 BOOST_ASSERT(!ex);
0203 return self->ready();
0204 }
0205
0206 template<typename Promise>
0207 std::coroutine_handle<void> await_suspend(std::coroutine_handle<Promise> h)
0208 {
0209 if (self->done)
0210 return std::noop_coroutine();
0211
0212
0213 if (!ex && self->awaited_from != nullptr)
0214 ex = already_awaited();
0215
0216 if (ex)
0217 return h;
0218
0219 if constexpr (requires (Promise p) {p.get_cancellation_slot();})
0220 if ((cl = h.promise().get_cancellation_slot()).is_connected())
0221 cl.emplace<forward_cancellation>(*self->cancel_signal);
0222
0223 self->awaited_from.reset(h.address());
0224
0225 std::coroutine_handle<void> res = std::noop_coroutine();
0226 if (self->yield_from != nullptr)
0227 res = self->yield_from.release();
0228
0229 if ((to_push.index() > 0) && !self->pushed_value && self->lazy)
0230 {
0231 if constexpr (std::is_void_v<Push>)
0232 self->pushed_value = true;
0233 else
0234 {
0235 if (to_push.index() == 1)
0236 self->pushed_value.emplace(std::move(*variant2::get<1>(to_push)));
0237 else
0238 {
0239 if constexpr (std::is_copy_constructible_v<Push>)
0240 self->pushed_value.emplace(std::move(*variant2::get<2>(to_push)));
0241 else
0242 {
0243 BOOST_ASSERT(!"push value is not movable");
0244 }
0245 }
0246 }
0247 to_push = variant2::monostate{};
0248 }
0249 return std::coroutine_handle<void>::from_address(res.address());
0250 }
0251
0252 Yield await_resume(const boost::source_location & loc = BOOST_CURRENT_LOCATION)
0253 {
0254 return await_resume(as_result_tag{}).value(loc);
0255 }
0256
0257 std::tuple<std::exception_ptr, Yield> await_resume(
0258 const as_tuple_tag &)
0259 {
0260 auto res = await_resume(as_result_tag{});
0261 if (res.has_error())
0262 return {res.error(), Yield{}};
0263 else
0264 return {nullptr, res.value()};
0265 }
0266
0267 system::result<Yield, std::exception_ptr> await_resume(const as_result_tag& )
0268 {
0269 if (cl.is_connected())
0270 cl.clear();
0271 if (ex)
0272 return {system::in_place_error, ex};
0273 if (self->exception)
0274 return {system::in_place_error, std::exchange(self->exception, nullptr)};
0275 if (!self->result)
0276 return {system::in_place_error, std::make_exception_ptr(std::runtime_error("cobalt::generator returned void"))};
0277
0278 if (to_push.index() > 0)
0279 {
0280 BOOST_ASSERT(!self->pushed_value);
0281 if constexpr (std::is_void_v<Push>)
0282 self->pushed_value = true;
0283 else
0284 {
0285 if (to_push.index() == 1)
0286 self->pushed_value.emplace(std::move(*variant2::get<1>(to_push)));
0287 else
0288 {
0289 if constexpr (std::is_copy_constructible_v<Push>)
0290 self->pushed_value.emplace(std::move(*variant2::get<2>(to_push)));
0291 else
0292 {
0293 BOOST_ASSERT(!"push value is not movable");
0294 }
0295 }
0296
0297 }
0298 to_push = variant2::monostate{};
0299 }
0300
0301
0302 if (self->yield_from != nullptr && !self->lazy)
0303 {
0304 auto exec = self->yield_from->get_executor();
0305 auto alloc = asio::get_associated_allocator(self->yield_from);
0306 asio::post(
0307 std::move(exec),
0308 asio::bind_allocator(
0309 alloc,
0310 [y = std::exchange(self->yield_from, nullptr)]() mutable
0311 {
0312 if (y->receiver)
0313 std::move(y)();
0314 }));
0315 }
0316
0317 return {system::in_place_value, self->get_result()};
0318 }
0319
0320 void interrupt_await() &
0321 {
0322 if (!self)
0323 return ;
0324 ex = detached_exception();
0325
0326 if (self->awaited_from)
0327 self->awaited_from.release().resume();
0328 }
0329 };
0330
0331 void interrupt_await() &
0332 {
0333 exception = detached_exception();
0334 awaited_from.release().resume();
0335 }
0336
0337 void rethrow_if()
0338 {
0339 if (exception)
0340 std::rethrow_exception(exception);
0341 }
0342 };
0343
0344 template<typename Yield, typename Push>
0345 struct generator_promise
0346 : promise_memory_resource_base,
0347 promise_cancellation_base<asio::cancellation_slot, asio::enable_total_cancellation>,
0348 promise_throw_if_cancelled_base,
0349 enable_awaitables<generator_promise<Yield, Push>>,
0350 enable_await_allocator<generator_promise<Yield, Push>>,
0351 enable_await_executor< generator_promise<Yield, Push>>,
0352 enable_await_deferred
0353 {
0354 using promise_cancellation_base<asio::cancellation_slot, asio::enable_total_cancellation>::await_transform;
0355 using promise_throw_if_cancelled_base::await_transform;
0356 using enable_awaitables<generator_promise<Yield, Push>>::await_transform;
0357 using enable_await_allocator<generator_promise<Yield, Push>>::await_transform;
0358 using enable_await_executor<generator_promise<Yield, Push>>::await_transform;
0359 using enable_await_deferred::await_transform;
0360
0361 [[nodiscard]] generator<Yield, Push> get_return_object()
0362 {
0363 return generator<Yield, Push>{this};
0364 }
0365
0366 mutable asio::cancellation_signal signal;
0367
0368 using executor_type = executor;
0369 executor_type exec;
0370 const executor_type & get_executor() const {return exec;}
0371
0372 template<typename ... Args>
0373 generator_promise(Args & ...args)
0374 :
0375 #if !defined(BOOST_COBALT_NO_PMR)
0376 promise_memory_resource_base(detail::get_memory_resource_from_args(args...)),
0377 #endif
0378 exec{detail::get_executor_from_args(args...)}
0379 {
0380 this->reset_cancellation_source(signal.slot());
0381 }
0382
0383 std::suspend_never initial_suspend() noexcept {return {};}
0384
0385 struct final_awaitable
0386 {
0387 generator_promise * generator;
0388 bool await_ready() const noexcept
0389 {
0390 return generator->receiver && generator->receiver->awaited_from.get() == nullptr;
0391 }
0392
0393 auto await_suspend(std::coroutine_handle<generator_promise> h) noexcept
0394 {
0395 std::coroutine_handle<void> res = std::noop_coroutine();
0396 if (generator->receiver && generator->receiver->awaited_from.get() != nullptr)
0397 res = generator->receiver->awaited_from.release();
0398 if (generator->receiver)
0399 generator->receiver->done = true;
0400
0401
0402 if (auto & rec = h.promise().receiver; rec != nullptr)
0403 {
0404 if (!rec->done && !rec->exception)
0405 rec->exception = detail::completed_unexpected();
0406 rec->done = true;
0407 rec->awaited_from.reset(nullptr);
0408 rec = nullptr;
0409 }
0410
0411 detail::self_destroy(h);
0412 return res;
0413 }
0414
0415 void await_resume() noexcept
0416 {
0417 if (generator->receiver)
0418 generator->receiver->done = true;
0419 }
0420 };
0421
0422 auto final_suspend() noexcept
0423 {
0424 return final_awaitable{this};
0425 }
0426
0427 void unhandled_exception()
0428 {
0429 if (this->receiver)
0430 this->receiver->exception = std::current_exception();
0431 else
0432 throw ;
0433 }
0434
0435 void return_value(const Yield & res) requires std::is_copy_constructible_v<Yield>
0436 {
0437 if (this->receiver)
0438 this->receiver->yield_value(res);
0439 }
0440
0441 void return_value(Yield && res)
0442 {
0443 if (this->receiver)
0444 this->receiver->yield_value(std::move(res));
0445 }
0446
0447 generator_receiver<Yield, Push>* receiver{nullptr};
0448
0449 auto await_transform(this_coro::initial_t val)
0450 {
0451 if(receiver)
0452 {
0453 receiver->lazy = true;
0454 return receiver->get_yield_awaitable(this);
0455 }
0456 else
0457 return generator_receiver<Yield, Push>::terminator();
0458 }
0459
0460 template<typename Yield_>
0461 auto yield_value(Yield_ && ret)
0462 {
0463 if(receiver)
0464 {
0465
0466 receiver->yield_value(std::forward<Yield_>(ret));
0467 return receiver->get_yield_awaitable(this);
0468 }
0469 else
0470 return generator_receiver<Yield, Push>::terminator();
0471 }
0472
0473 void interrupt_await() &
0474 {
0475 if (this->receiver)
0476 {
0477 this->receiver->exception = detached_exception();
0478 std::coroutine_handle<void>::from_address(this->receiver->awaited_from.release()).resume();
0479 }
0480 }
0481
0482 ~generator_promise()
0483 {
0484 if (this->receiver)
0485 {
0486 if (!this->receiver->done && !this->receiver->exception)
0487 this->receiver->exception = detail::completed_unexpected();
0488 this->receiver->done = true;
0489 this->receiver->awaited_from.reset(nullptr);
0490 }
0491 }
0492
0493 };
0494
0495 template<typename Yield, typename Push>
0496 struct generator_yield_awaitable
0497 {
0498 generator_promise<Yield, Push> *self;
0499 constexpr bool await_ready() const
0500 {
0501 return self && self->receiver && self->receiver->pushed_value && !self->receiver->result;
0502 }
0503
0504 std::coroutine_handle<void> await_suspend(
0505 std::coroutine_handle<generator_promise<Yield, Push>> h
0506 #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
0507 , const boost::source_location & loc = BOOST_CURRENT_LOCATION
0508 #endif
0509 )
0510 {
0511 if (self == nullptr)
0512 {
0513 if (auto & rec = h.promise().receiver; rec != nullptr)
0514 {
0515 if (!rec->done && !rec->exception)
0516 rec->exception = detail::completed_unexpected();
0517 rec->done = true;
0518 rec->awaited_from.reset(nullptr);
0519 rec = nullptr;
0520 }
0521
0522 detail::self_destroy(h);
0523 return std::noop_coroutine();
0524 }
0525 std::coroutine_handle<void> res = std::noop_coroutine();
0526 if (self->receiver->awaited_from.get() != nullptr)
0527 res = self->receiver->awaited_from.release();
0528 #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
0529 self->receiver->yield_from.reset(&h.promise(), loc);
0530 #else
0531 self->receiver->yield_from.reset(&h.promise());
0532 #endif
0533 return res;
0534 }
0535
0536 Push await_resume()
0537 {
0538 BOOST_ASSERT(self->receiver);
0539 BOOST_ASSERT(self->receiver->pushed_value);
0540 return *std::exchange(self->receiver->pushed_value, std::nullopt);
0541 }
0542 };
0543
0544 template<typename Yield>
0545 struct generator_yield_awaitable<Yield, void>
0546 {
0547 generator_promise<Yield, void> *self;
0548 constexpr bool await_ready() { return self && self->receiver && self->receiver->pushed_value; }
0549
0550 std::coroutine_handle<> await_suspend(
0551 std::coroutine_handle<generator_promise<Yield, void>> h
0552 #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
0553 , const boost::source_location & loc = BOOST_CURRENT_LOCATION
0554 #endif
0555 )
0556 {
0557 if (self == nullptr)
0558 {
0559 if (auto & rec = h.promise().receiver; rec != nullptr)
0560 {
0561 if (!rec->done && !rec->exception)
0562 rec->exception = detail::completed_unexpected();
0563 rec->done = true;
0564 rec->awaited_from.reset(nullptr);
0565 rec = nullptr;
0566 }
0567 detail::self_destroy(h);
0568 return std::noop_coroutine();
0569 }
0570 std::coroutine_handle<void> res = std::noop_coroutine();
0571 BOOST_ASSERT(self);
0572 if (self->receiver->awaited_from.get() != nullptr)
0573 res = self->receiver->awaited_from.release();
0574 #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
0575 self->receiver->yield_from.reset(&h.promise(), loc);
0576 #else
0577 self->receiver->yield_from.reset(&h.promise());
0578 #endif
0579 return res;
0580 }
0581
0582 void await_resume()
0583 {
0584 BOOST_ASSERT(self->receiver->pushed_value);
0585 self->receiver->pushed_value = false;
0586 }
0587
0588 };
0589
0590
0591 template<typename Yield, typename Push>
0592 struct generator_base
0593 {
0594 auto operator()( Push && push)
0595 {
0596 return static_cast<generator<Yield, Push>*>(this)->receiver_.get_awaitable(std::move(push));
0597 }
0598 auto operator()(const Push & push) requires std::is_copy_constructible_v<Push>
0599 {
0600 return static_cast<generator<Yield, Push>*>(this)->receiver_.get_awaitable(push);
0601 }
0602 };
0603
0604 template<typename Yield>
0605 struct generator_base<Yield, void>
0606 {
0607 auto operator co_await ()
0608 {
0609 return static_cast<generator<Yield, void>*>(this)->receiver_.get_awaitable();
0610 }
0611 };
0612
0613 template<typename T>
0614 struct generator_with_awaitable
0615 {
0616 generator_base<T, void> &g;
0617 std::optional<typename detail::generator_receiver<T, void>::awaitable> awaitable;
0618
0619 template<typename Promise>
0620 void await_suspend(std::coroutine_handle<Promise> h)
0621 {
0622 g.cancel();
0623 awaitable.emplace(g.operator co_await());
0624 return awaitable->await_suspend(h);
0625 }
0626
0627 void await_resume() {}
0628
0629 };
0630
0631 }
0632
0633 }
0634
0635 #endif