Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-16 09:43:20

0001 //
0002 // experimental/coro.hpp
0003 // ~~~~~~~~~~~~~~~~~~~~~
0004 //
0005 // Copyright (c) 2021-2023 Klemens D. Morgenstern
0006 //                         (klemens dot morgenstern at gmx dot net)
0007 //
0008 // Distributed under the Boost Software License, Version 1.0. (See accompanying
0009 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
0010 //
0011 
0012 #ifndef BOOST_ASIO_EXPERIMENTAL_CORO_HPP
0013 #define BOOST_ASIO_EXPERIMENTAL_CORO_HPP
0014 
0015 #if defined(_MSC_VER) && (_MSC_VER >= 1200)
0016 # pragma once
0017 #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
0018 
0019 #include <boost/asio/detail/config.hpp>
0020 #include <boost/asio/dispatch.hpp>
0021 #include <boost/asio/error.hpp>
0022 #include <boost/system/error_code.hpp>
0023 #include <boost/asio/experimental/coro_traits.hpp>
0024 #include <boost/asio/experimental/detail/coro_promise_allocator.hpp>
0025 #include <boost/asio/experimental/detail/partial_promise.hpp>
0026 #include <boost/asio/post.hpp>
0027 
0028 #include <boost/asio/detail/push_options.hpp>
0029 
0030 namespace boost {
0031 namespace asio {
0032 namespace experimental {
0033 namespace detail {
0034 
0035 template <typename Signature, typename Return,
0036     typename Executor, typename Allocator>
0037 struct coro_promise;
0038 
0039 template <typename T, typename Coroutine>
0040 struct coro_with_arg;
0041 
0042 } // namespace detail
0043 
0044 /// The main type of a resumable coroutine.
0045 /**
0046  * Template parameter @c Yield specifies type or signature used by co_yield,
0047  * @c Return specifies the type used for co_return, and @c Executor specifies
0048  * the underlying executor type.
0049  */
0050 template <typename Yield = void, typename Return = void,
0051     typename Executor = any_io_executor,
0052     typename Allocator = std::allocator<void>>
0053 struct coro
0054 {
0055   /// The traits of the coroutine. See boost::asio::experimental::coro_traits
0056   /// for details.
0057   using traits = coro_traits<Yield, Return, Executor>;
0058 
0059   /// The value that can be passed into a symmetrical cororoutine. @c void if
0060   /// asymmetrical.
0061   using input_type = typename traits::input_type;
0062 
0063   /// The type that can be passed out through a co_yield.
0064   using yield_type = typename traits::yield_type;
0065 
0066   /// The type that can be passed out through a co_return.
0067   using return_type = typename traits::return_type;
0068 
0069   /// The type received by a co_await or async_resume. Its a combination of
0070   /// yield and return.
0071   using result_type = typename traits::result_type;
0072 
0073   /// The signature used by the async_resume.
0074   using signature_type = typename traits::signature_type;
0075 
0076   /// Whether or not the coroutine is noexcept.
0077   constexpr static bool is_noexcept = traits::is_noexcept;
0078 
0079   /// The error type of the coroutine. Void for noexcept
0080   using error_type = typename traits::error_type;
0081 
0082   /// Completion handler type used by async_resume.
0083   using completion_handler = typename traits::completion_handler;
0084 
0085   /// The internal promise-type of the coroutine.
0086   using promise_type = detail::coro_promise<Yield, Return, Executor, Allocator>;
0087 
0088 #if !defined(GENERATING_DOCUMENTATION)
0089   template <typename T, typename Coroutine>
0090   friend struct detail::coro_with_arg;
0091 #endif // !defined(GENERATING_DOCUMENTATION)
0092 
0093   /// The executor type.
0094   using executor_type = Executor;
0095 
0096   /// The allocator type.
0097   using allocator_type = Allocator;
0098 
0099 #if !defined(GENERATING_DOCUMENTATION)
0100   friend struct detail::coro_promise<Yield, Return, Executor, Allocator>;
0101 #endif // !defined(GENERATING_DOCUMENTATION)
0102 
0103   /// The default constructor, gives an invalid coroutine.
0104   coro() = default;
0105 
0106   /// Move constructor.
0107   coro(coro&& lhs) noexcept
0108     : coro_(std::exchange(lhs.coro_, nullptr))
0109   {
0110   }
0111 
0112   coro(const coro&) = delete;
0113 
0114   /// Move assignment.
0115   coro& operator=(coro&& lhs) noexcept
0116   {
0117     std::swap(coro_, lhs.coro_);
0118     return *this;
0119   }
0120 
0121   coro& operator=(const coro&) = delete;
0122 
0123   /// Destructor. Destroys the coroutine, if it holds a valid one.
0124   /**
0125    * @note This does not cancel an active coroutine. Destructing a resumable
0126    * coroutine, i.e. one with a call to async_resume that has not completed, is
0127    * undefined behaviour.
0128    */
0129   ~coro()
0130   {
0131     if (coro_ != nullptr)
0132     {
0133       struct destroyer
0134       {
0135         detail::coroutine_handle<promise_type> handle;
0136 
0137         destroyer(const detail::coroutine_handle<promise_type>& handle)
0138           : handle(handle)
0139         { }
0140 
0141         destroyer(destroyer&& lhs)
0142           : handle(std::exchange(lhs.handle, nullptr))
0143         {
0144         }
0145 
0146         destroyer(const destroyer&) = delete;
0147 
0148         void operator()() {}
0149 
0150         ~destroyer()
0151         {
0152           if (handle)
0153             handle.destroy();
0154         }
0155       };
0156 
0157       auto handle =
0158         detail::coroutine_handle<promise_type>::from_promise(*coro_);
0159       if (handle)
0160         boost::asio::dispatch(coro_->get_executor(), destroyer{handle});
0161     }
0162   }
0163 
0164   /// Get the used executor.
0165   executor_type get_executor() const
0166   {
0167     if (coro_)
0168       return coro_->get_executor();
0169 
0170     if constexpr (std::is_default_constructible_v<Executor>)
0171       return Executor{};
0172     else
0173       throw std::logic_error("Coroutine has no executor");
0174   }
0175 
0176   /// Get the used allocator.
0177   allocator_type get_allocator() const
0178   {
0179     if (coro_)
0180       return coro_->get_allocator();
0181 
0182     if constexpr (std::is_default_constructible_v<Allocator>)
0183       return Allocator{};
0184     else
0185       throw std::logic_error(
0186           "Coroutine has no available allocator without a constructed promise");
0187   }
0188 
0189   /// Resume the coroutine.
0190   /**
0191    * @param token The completion token of the async resume.
0192    *
0193    * @attention Calling an invalid coroutine with a noexcept signature is
0194    * undefined behaviour.
0195    *
0196    * @note This overload is only available for coroutines without an input
0197    * value.
0198    */
0199   template <typename CompletionToken>
0200     requires std::is_void_v<input_type>
0201   auto async_resume(CompletionToken&& token) &
0202   {
0203     return async_initiate<CompletionToken,
0204         typename traits::completion_handler>(
0205           initiate_async_resume(this), token);
0206   }
0207 
0208   /// Resume the coroutine.
0209   /**
0210    * @param token The completion token of the async resume.
0211    *
0212    * @attention Calling an invalid coroutine with a noexcept signature is
0213    * undefined behaviour.
0214    *
0215    * @note This overload is only available for coroutines with an input value.
0216    */
0217   template <typename CompletionToken, detail::convertible_to<input_type> T>
0218   auto async_resume(T&& ip, CompletionToken&& token) &
0219   {
0220     return async_initiate<CompletionToken,
0221         typename traits::completion_handler>(
0222           initiate_async_resume(this), token, std::forward<T>(ip));
0223   }
0224 
0225   /// Operator used for coroutines without input value.
0226   auto operator co_await() requires (std::is_void_v<input_type>)
0227   {
0228     return awaitable_t{*this};
0229   }
0230 
0231   /// Operator used for coroutines with input value.
0232   /**
0233    * @param ip The input value
0234    *
0235    * @returns An awaitable handle.
0236    *
0237    * @code
0238    * coro<void> push_values(coro<double(int)> c)
0239    * {
0240    *    std::optional<double> res = co_await c(42);
0241    * }
0242    * @endcode
0243    */
0244   template <detail::convertible_to<input_type> T>
0245   auto operator()(T&& ip)
0246   {
0247     return detail::coro_with_arg<std::decay_t<T>, coro>{
0248         std::forward<T>(ip), *this};
0249   }
0250 
0251   /// Check whether the coroutine is open, i.e. can be resumed.
0252   bool is_open() const
0253   {
0254     if (coro_)
0255     {
0256       auto handle =
0257         detail::coroutine_handle<promise_type>::from_promise(*coro_);
0258       return handle && !handle.done();
0259     }
0260     else
0261       return false;
0262   }
0263 
0264   /// Check whether the coroutine is open, i.e. can be resumed.
0265   explicit operator bool() const { return is_open(); }
0266 
0267 private:
0268   struct awaitable_t;
0269 
0270   struct initiate_async_resume;
0271 
0272   explicit coro(promise_type* const cr) : coro_(cr) {}
0273 
0274   promise_type* coro_{nullptr};
0275 };
0276 
0277 /// A generator is a coro that returns void and yields value.
0278 template<typename T, typename Executor = boost::asio::any_io_executor,
0279     typename Allocator = std::allocator<void>>
0280 using generator = coro<T, void, Executor, Allocator>;
0281 
0282 /// A task is a coro that does not yield values
0283 template<typename T, typename Executor = boost::asio::any_io_executor,
0284     typename Allocator = std::allocator<void>>
0285 using task = coro<void(), T, Executor, Allocator>;
0286 
0287 } // namespace experimental
0288 } // namespace asio
0289 } // namespace boost
0290 
0291 #include <boost/asio/detail/pop_options.hpp>
0292 
0293 #include <boost/asio/experimental/impl/coro.hpp>
0294 
0295 #endif // BOOST_ASIO_EXPERIMENTAL_CORO_HPP