File indexing completed on 2025-12-16 09:43:20
0001
0002
0003
0004
0005
0006
0007
0008
0009
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
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 }
0043
0044
0045
0046
0047
0048
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
0056
0057 using traits = coro_traits<Yield, Return, Executor>;
0058
0059
0060
0061 using input_type = typename traits::input_type;
0062
0063
0064 using yield_type = typename traits::yield_type;
0065
0066
0067 using return_type = typename traits::return_type;
0068
0069
0070
0071 using result_type = typename traits::result_type;
0072
0073
0074 using signature_type = typename traits::signature_type;
0075
0076
0077 constexpr static bool is_noexcept = traits::is_noexcept;
0078
0079
0080 using error_type = typename traits::error_type;
0081
0082
0083 using completion_handler = typename traits::completion_handler;
0084
0085
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
0092
0093
0094 using executor_type = Executor;
0095
0096
0097 using allocator_type = Allocator;
0098
0099 #if !defined(GENERATING_DOCUMENTATION)
0100 friend struct detail::coro_promise<Yield, Return, Executor, Allocator>;
0101 #endif
0102
0103
0104 coro() = default;
0105
0106
0107 coro(coro&& lhs) noexcept
0108 : coro_(std::exchange(lhs.coro_, nullptr))
0109 {
0110 }
0111
0112 coro(const coro&) = delete;
0113
0114
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
0124
0125
0126
0127
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
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
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
0190
0191
0192
0193
0194
0195
0196
0197
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
0209
0210
0211
0212
0213
0214
0215
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
0226 auto operator co_await() requires (std::is_void_v<input_type>)
0227 {
0228 return awaitable_t{*this};
0229 }
0230
0231
0232
0233
0234
0235
0236
0237
0238
0239
0240
0241
0242
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
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
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
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
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 }
0288 }
0289 }
0290
0291 #include <boost/asio/detail/pop_options.hpp>
0292
0293 #include <boost/asio/experimental/impl/coro.hpp>
0294
0295 #endif