Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-16 09:44:24

0001 //
0002 // Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net)
0003 //
0004 // Distributed under the Boost Software License, Version 1.0. (See accompanying
0005 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
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) // ok, so we're actually done already, so noop
0210         return std::noop_coroutine();
0211 
0212 
0213       if (!ex && self->awaited_from != nullptr) // generator already being awaited, that's an error!
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) // missing co_return this is accepted behaviour, if the compiler agrees
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       // now we also want to resume the coroutine, so it starts work
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) // make sure we only resume eagerly when attached to a generator object
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       // if this is lazy, there might still be a value in there.
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) // we're a terminator, kill it
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) // we're a terminator, kill it
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 //BOOST_COBALT_DETAIL_GENERATOR_HPP