100.00% Lines (74/74) 100.00% Functions (24/24)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2026 Steve Gerbino 2   // Copyright (c) 2026 Steve Gerbino
3   // Copyright (c) 2026 Michael Vandeberg 3   // Copyright (c) 2026 Michael Vandeberg
4   // 4   //
5   // Distributed under the Boost Software License, Version 1.0. (See accompanying 5   // Distributed under the Boost Software License, Version 1.0. (See accompanying
6   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7   // 7   //
8   // Official repository: https://github.com/cppalliance/corosio 8   // Official repository: https://github.com/cppalliance/corosio
9   // 9   //
10   10  
11   #ifndef BOOST_COROSIO_NATIVE_NATIVE_TCP_SOCKET_HPP 11   #ifndef BOOST_COROSIO_NATIVE_NATIVE_TCP_SOCKET_HPP
12   #define BOOST_COROSIO_NATIVE_NATIVE_TCP_SOCKET_HPP 12   #define BOOST_COROSIO_NATIVE_NATIVE_TCP_SOCKET_HPP
13   13  
14   #include <boost/corosio/tcp_socket.hpp> 14   #include <boost/corosio/tcp_socket.hpp>
15   #include <boost/corosio/backend.hpp> 15   #include <boost/corosio/backend.hpp>
16   16  
17   #ifndef BOOST_COROSIO_MRDOCS 17   #ifndef BOOST_COROSIO_MRDOCS
18   #if BOOST_COROSIO_HAS_EPOLL 18   #if BOOST_COROSIO_HAS_EPOLL
19   #include <boost/corosio/native/detail/epoll/epoll_types.hpp> 19   #include <boost/corosio/native/detail/epoll/epoll_types.hpp>
20   #endif 20   #endif
21   21  
22   #if BOOST_COROSIO_HAS_SELECT 22   #if BOOST_COROSIO_HAS_SELECT
23   #include <boost/corosio/native/detail/select/select_types.hpp> 23   #include <boost/corosio/native/detail/select/select_types.hpp>
24   #endif 24   #endif
25   25  
26   #if BOOST_COROSIO_HAS_KQUEUE 26   #if BOOST_COROSIO_HAS_KQUEUE
27   #include <boost/corosio/native/detail/kqueue/kqueue_types.hpp> 27   #include <boost/corosio/native/detail/kqueue/kqueue_types.hpp>
28   #endif 28   #endif
29   29  
30   #if BOOST_COROSIO_HAS_IOCP 30   #if BOOST_COROSIO_HAS_IOCP
31   #include <boost/corosio/native/detail/iocp/win_tcp_acceptor_service.hpp> 31   #include <boost/corosio/native/detail/iocp/win_tcp_acceptor_service.hpp>
32   #endif 32   #endif
33   33  
34 - #if BOOST_COROSIO_HAS_URING 34 + #if BOOST_COROSIO_HAS_IO_URING
35 - #include <boost/corosio/native/detail/uring/uring_types.hpp> 35 + #include <boost/corosio/native/detail/io_uring/io_uring_types.hpp>
36   #endif 36   #endif
37   #endif // !BOOST_COROSIO_MRDOCS 37   #endif // !BOOST_COROSIO_MRDOCS
38   38  
39   namespace boost::corosio { 39   namespace boost::corosio {
40   40  
41   /** An asynchronous TCP socket with devirtualized I/O operations. 41   /** An asynchronous TCP socket with devirtualized I/O operations.
42   42  
43   This class template inherits from @ref tcp_socket and shadows 43   This class template inherits from @ref tcp_socket and shadows
44   the async operations (`read_some`, `write_some`, `connect`) with 44   the async operations (`read_some`, `write_some`, `connect`) with
45   versions that call the backend implementation directly, allowing 45   versions that call the backend implementation directly, allowing
46   the compiler to inline through the entire call chain. 46   the compiler to inline through the entire call chain.
47   47  
48   Non-async operations (`open`, `close`, `cancel`, socket options) 48   Non-async operations (`open`, `close`, `cancel`, socket options)
49   remain unchanged and dispatch through the compiled library. 49   remain unchanged and dispatch through the compiled library.
50   50  
51   A `native_tcp_socket` IS-A `tcp_socket` and can be passed to 51   A `native_tcp_socket` IS-A `tcp_socket` and can be passed to
52   any function expecting `tcp_socket&` or `io_stream&`, in which 52   any function expecting `tcp_socket&` or `io_stream&`, in which
53   case virtual dispatch is used transparently. 53   case virtual dispatch is used transparently.
54   54  
55   @tparam Backend A backend tag value (e.g., `epoll`, 55   @tparam Backend A backend tag value (e.g., `epoll`,
56   `iocp`) whose type provides the concrete implementation 56   `iocp`) whose type provides the concrete implementation
57   types. 57   types.
58   58  
59   @par Thread Safety 59   @par Thread Safety
60   Same as @ref tcp_socket. 60   Same as @ref tcp_socket.
61   61  
62   @par Example 62   @par Example
63   @par !example native_tcp_socket 63   @par !example native_tcp_socket
64   64  
65   @see tcp_socket, epoll_t, iocp_t 65   @see tcp_socket, epoll_t, iocp_t
66   */ 66   */
67   template<auto Backend> 67   template<auto Backend>
68   class native_tcp_socket : public tcp_socket 68   class native_tcp_socket : public tcp_socket
69   { 69   {
70   using backend_type = decltype(Backend); 70   using backend_type = decltype(Backend);
71   using impl_type = typename backend_type::tcp_socket_type; 71   using impl_type = typename backend_type::tcp_socket_type;
72   using service_type = typename backend_type::tcp_service_type; 72   using service_type = typename backend_type::tcp_service_type;
73   73  
HITCBC 74   49 impl_type& get_impl() noexcept 74   49 impl_type& get_impl() noexcept
75   { 75   {
HITCBC 76   49 return *static_cast<impl_type*>(h_.get()); 76   49 return *static_cast<impl_type*>(h_.get());
77   } 77   }
78   78  
79   template<class MutableBufferSequence> 79   template<class MutableBufferSequence>
80   struct native_read_awaitable 80   struct native_read_awaitable
81   { 81   {
82   native_tcp_socket& self_; 82   native_tcp_socket& self_;
83   MutableBufferSequence buffers_; 83   MutableBufferSequence buffers_;
84   std::stop_token token_; 84   std::stop_token token_;
85   mutable std::error_code ec_; 85   mutable std::error_code ec_;
86   mutable std::size_t bytes_transferred_ = 0; 86   mutable std::size_t bytes_transferred_ = 0;
87   87  
HITCBC 88   10 native_read_awaitable( 88   10 native_read_awaitable(
89   native_tcp_socket& self, MutableBufferSequence buffers) noexcept 89   native_tcp_socket& self, MutableBufferSequence buffers) noexcept
HITCBC 90   10 : self_(self) 90   10 : self_(self)
HITCBC 91   10 , buffers_(std::move(buffers)) 91   10 , buffers_(std::move(buffers))
92   { 92   {
HITCBC 93   10 } 93   10 }
94   94  
HITCBC 95   10 bool await_ready() const noexcept 95   10 bool await_ready() const noexcept
96   { 96   {
97   // A pre-set ec_ means the initiator failed before 97   // A pre-set ec_ means the initiator failed before
98   // dispatch (e.g. a closed object). 98   // dispatch (e.g. a closed object).
HITCBC 99   10 return static_cast<bool>(ec_) || token_.stop_requested(); 99   10 return static_cast<bool>(ec_) || token_.stop_requested();
100   } 100   }
101   101  
HITCBC 102   10 [[nodiscard]] capy::io_result<std::size_t> await_resume() const noexcept 102   10 [[nodiscard]] capy::io_result<std::size_t> await_resume() const noexcept
103   { 103   {
HITCBC 104   10 if (token_.stop_requested()) 104   10 if (token_.stop_requested())
HITCBC 105   4 return {make_error_code(std::errc::operation_canceled), 0}; 105   4 return {make_error_code(std::errc::operation_canceled), 0};
HITCBC 106   6 return {ec_, bytes_transferred_}; 106   6 return {ec_, bytes_transferred_};
107   } 107   }
108   108  
HITCBC 109   10 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 109   10 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
110   -> std::coroutine_handle<> 110   -> std::coroutine_handle<>
111   { 111   {
HITCBC 112   10 token_ = env->stop_token; 112   10 token_ = env->stop_token;
HITCBC 113   30 return self_.get_impl().read_some( 113   30 return self_.get_impl().read_some(
HITCBC 114   30 h, env->executor, buffers_, token_, &ec_, &bytes_transferred_); 114   30 h, env->executor, buffers_, token_, &ec_, &bytes_transferred_);
115   } 115   }
116   }; 116   };
117   117  
118   template<class ConstBufferSequence> 118   template<class ConstBufferSequence>
119   struct native_write_awaitable 119   struct native_write_awaitable
120   { 120   {
121   native_tcp_socket& self_; 121   native_tcp_socket& self_;
122   ConstBufferSequence buffers_; 122   ConstBufferSequence buffers_;
123   std::stop_token token_; 123   std::stop_token token_;
124   mutable std::error_code ec_; 124   mutable std::error_code ec_;
125   mutable std::size_t bytes_transferred_ = 0; 125   mutable std::size_t bytes_transferred_ = 0;
126   126  
HITCBC 127   12 native_write_awaitable( 127   12 native_write_awaitable(
128   native_tcp_socket& self, ConstBufferSequence buffers) noexcept 128   native_tcp_socket& self, ConstBufferSequence buffers) noexcept
HITCBC 129   12 : self_(self) 129   12 : self_(self)
HITCBC 130   12 , buffers_(std::move(buffers)) 130   12 , buffers_(std::move(buffers))
131   { 131   {
HITCBC 132   12 } 132   12 }
133   133  
HITCBC 134   12 bool await_ready() const noexcept 134   12 bool await_ready() const noexcept
135   { 135   {
136   // A pre-set ec_ means the initiator failed before 136   // A pre-set ec_ means the initiator failed before
137   // dispatch (e.g. a closed object). 137   // dispatch (e.g. a closed object).
HITCBC 138   12 return static_cast<bool>(ec_) || token_.stop_requested(); 138   12 return static_cast<bool>(ec_) || token_.stop_requested();
139   } 139   }
140   140  
HITCBC 141   12 [[nodiscard]] capy::io_result<std::size_t> await_resume() const noexcept 141   12 [[nodiscard]] capy::io_result<std::size_t> await_resume() const noexcept
142   { 142   {
HITCBC 143   12 if (token_.stop_requested()) 143   12 if (token_.stop_requested())
HITCBC 144   2 return {make_error_code(std::errc::operation_canceled), 0}; 144   2 return {make_error_code(std::errc::operation_canceled), 0};
HITCBC 145   10 return {ec_, bytes_transferred_}; 145   10 return {ec_, bytes_transferred_};
146   } 146   }
147   147  
HITCBC 148   12 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 148   12 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
149   -> std::coroutine_handle<> 149   -> std::coroutine_handle<>
150   { 150   {
HITCBC 151   12 token_ = env->stop_token; 151   12 token_ = env->stop_token;
HITCBC 152   36 return self_.get_impl().write_some( 152   36 return self_.get_impl().write_some(
HITCBC 153   36 h, env->executor, buffers_, token_, &ec_, &bytes_transferred_); 153   36 h, env->executor, buffers_, token_, &ec_, &bytes_transferred_);
154   } 154   }
155   }; 155   };
156   156  
157   struct native_wait_awaitable 157   struct native_wait_awaitable
158   { 158   {
159   native_tcp_socket& self_; 159   native_tcp_socket& self_;
160   wait_type w_; 160   wait_type w_;
161   std::stop_token token_; 161   std::stop_token token_;
162   mutable std::error_code ec_; 162   mutable std::error_code ec_;
163   163  
HITCBC 164   6 native_wait_awaitable(native_tcp_socket& self, wait_type w) noexcept 164   6 native_wait_awaitable(native_tcp_socket& self, wait_type w) noexcept
HITCBC 165   6 : self_(self) 165   6 : self_(self)
HITCBC 166   6 , w_(w) 166   6 , w_(w)
167   { 167   {
HITCBC 168   6 } 168   6 }
169   169  
HITCBC 170   6 bool await_ready() const noexcept 170   6 bool await_ready() const noexcept
171   { 171   {
172   // A pre-set ec_ means the initiator failed before 172   // A pre-set ec_ means the initiator failed before
173   // dispatch (e.g. a closed object). 173   // dispatch (e.g. a closed object).
HITCBC 174   6 return static_cast<bool>(ec_) || token_.stop_requested(); 174   6 return static_cast<bool>(ec_) || token_.stop_requested();
175   } 175   }
176   176  
HITCBC 177   6 [[nodiscard]] capy::io_result<> await_resume() const noexcept 177   6 [[nodiscard]] capy::io_result<> await_resume() const noexcept
178   { 178   {
HITCBC 179   6 if (token_.stop_requested()) 179   6 if (token_.stop_requested())
HITCBC 180   2 return {make_error_code(std::errc::operation_canceled)}; 180   2 return {make_error_code(std::errc::operation_canceled)};
HITCBC 181   4 return {ec_}; 181   4 return {ec_};
182   } 182   }
183   183  
HITCBC 184   6 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 184   6 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
185   -> std::coroutine_handle<> 185   -> std::coroutine_handle<>
186   { 186   {
HITCBC 187   6 token_ = env->stop_token; 187   6 token_ = env->stop_token;
HITCBC 188 - 6 return self_.get_impl().wait(h, env->executor, w_, token_, &ec_); 188 + 18 return self_.get_impl().wait(
HITGNC   189 + 18 h, env->executor, w_, token_, &ec_);
189   } 190   }
190   }; 191   };
191   192  
192   struct native_connect_awaitable 193   struct native_connect_awaitable
193   { 194   {
194   native_tcp_socket& self_; 195   native_tcp_socket& self_;
195   endpoint endpoint_; 196   endpoint endpoint_;
196   std::stop_token token_; 197   std::stop_token token_;
197   mutable std::error_code ec_; 198   mutable std::error_code ec_;
198   199  
HITCBC 199   21 native_connect_awaitable(native_tcp_socket& self, endpoint ep) noexcept 200   21 native_connect_awaitable(native_tcp_socket& self, endpoint ep) noexcept
HITCBC 200   21 : self_(self) 201   21 : self_(self)
HITCBC 201   21 , endpoint_(ep) 202   21 , endpoint_(ep)
202   { 203   {
HITCBC 203   21 } 204   21 }
204   205  
HITCBC 205   21 bool await_ready() const noexcept 206   21 bool await_ready() const noexcept
206   { 207   {
207   // A pre-set ec_ means the initiator failed before 208   // A pre-set ec_ means the initiator failed before
208   // dispatch (e.g. a closed object). 209   // dispatch (e.g. a closed object).
HITCBC 209   21 return static_cast<bool>(ec_) || token_.stop_requested(); 210   21 return static_cast<bool>(ec_) || token_.stop_requested();
210   } 211   }
211   212  
HITCBC 212   21 [[nodiscard]] capy::io_result<> await_resume() const noexcept 213   21 [[nodiscard]] capy::io_result<> await_resume() const noexcept
213   { 214   {
HITCBC 214   21 if (token_.stop_requested()) 215   21 if (token_.stop_requested())
HITCBC 215   2 return {make_error_code(std::errc::operation_canceled)}; 216   2 return {make_error_code(std::errc::operation_canceled)};
HITCBC 216   19 return {ec_}; 217   19 return {ec_};
217   } 218   }
218   219  
HITCBC 219   21 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 220   21 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
220   -> std::coroutine_handle<> 221   -> std::coroutine_handle<>
221   { 222   {
HITCBC 222   21 token_ = env->stop_token; 223   21 token_ = env->stop_token;
HITCBC 223   63 return self_.get_impl().connect( 224   63 return self_.get_impl().connect(
HITCBC 224   63 h, env->executor, endpoint_, token_, &ec_); 225   63 h, env->executor, endpoint_, token_, &ec_);
225   } 226   }
226   }; 227   };
227   228  
228   public: 229   public:
229   /** Construct a native socket from an execution context. 230   /** Construct a native socket from an execution context.
230   231  
231   @param ctx The execution context that will own this socket. 232   @param ctx The execution context that will own this socket.
232   */ 233   */
HITCBC 233   45 explicit native_tcp_socket(capy::execution_context& ctx) 234   45 explicit native_tcp_socket(capy::execution_context& ctx)
HITCBC 234   45 : io_object(create_handle<service_type>(ctx)) 235   45 : io_object(create_handle<service_type>(ctx))
235   { 236   {
HITCBC 236   45 } 237   45 }
237   238  
238   /** Construct a native socket from an executor. 239   /** Construct a native socket from an executor.
239   240  
240   @param ex The executor whose context will own the socket. 241   @param ex The executor whose context will own the socket.
241   */ 242   */
242   template<class Ex> 243   template<class Ex>
243   requires(!std::same_as<std::remove_cvref_t<Ex>, native_tcp_socket>) && 244   requires(!std::same_as<std::remove_cvref_t<Ex>, native_tcp_socket>) &&
244   capy::Executor<Ex> 245   capy::Executor<Ex>
245   explicit native_tcp_socket(Ex const& ex) : native_tcp_socket(ex.context()) 246   explicit native_tcp_socket(Ex const& ex) : native_tcp_socket(ex.context())
246   { 247   {
247   } 248   }
248   249  
249   /** Move construct. 250   /** Move construct.
250   251  
251   @param other The socket to move from. 252   @param other The socket to move from.
252   253  
253   @pre No awaitables returned by @p other's methods exist. 254   @pre No awaitables returned by @p other's methods exist.
254   @pre @p other is not referenced as a peer in any outstanding 255   @pre @p other is not referenced as a peer in any outstanding
255   accept awaitable. 256   accept awaitable.
256   @pre The execution context associated with @p other must 257   @pre The execution context associated with @p other must
257   outlive this socket. 258   outlive this socket.
258   */ 259   */
HITCBC 259   24 native_tcp_socket(native_tcp_socket&&) noexcept = default; 260   24 native_tcp_socket(native_tcp_socket&&) noexcept = default;
260   261  
261   /** Move assign. 262   /** Move assign.
262   263  
263   @param other The socket to move from. 264   @param other The socket to move from.
264   265  
265   @pre No awaitables returned by either `*this` or @p other's 266   @pre No awaitables returned by either `*this` or @p other's
266   methods exist. 267   methods exist.
267   @pre Neither `*this` nor @p other is referenced as a peer in 268   @pre Neither `*this` nor @p other is referenced as a peer in
268   any outstanding accept awaitable. 269   any outstanding accept awaitable.
269   @pre The execution context associated with @p other must 270   @pre The execution context associated with @p other must
270   outlive this socket. 271   outlive this socket.
271   */ 272   */
HITCBC 272   3 native_tcp_socket& operator=(native_tcp_socket&&) noexcept = default; 273   3 native_tcp_socket& operator=(native_tcp_socket&&) noexcept = default;
273   274  
274   native_tcp_socket(native_tcp_socket const&) = delete; 275   native_tcp_socket(native_tcp_socket const&) = delete;
275   native_tcp_socket& operator=(native_tcp_socket const&) = delete; 276   native_tcp_socket& operator=(native_tcp_socket const&) = delete;
276   277  
277   /** Asynchronously read data from the socket. 278   /** Asynchronously read data from the socket.
278   279  
279   Calls the backend implementation directly, bypassing virtual 280   Calls the backend implementation directly, bypassing virtual
280   dispatch. Otherwise identical to @ref io_stream::read_some. 281   dispatch. Otherwise identical to @ref io_stream::read_some.
281   282  
282   @param buffers The buffer sequence to read into. 283   @param buffers The buffer sequence to read into.
283   284  
284   @return An awaitable yielding `(error_code, std::size_t)`. 285   @return An awaitable yielding `(error_code, std::size_t)`.
285   286  
286   This socket must outlive the returned awaitable. The memory 287   This socket must outlive the returned awaitable. The memory
287   referenced by @p buffers must remain valid until the operation 288   referenced by @p buffers must remain valid until the operation
288   completes. 289   completes.
289   */ 290   */
290   template<capy::MutableBufferSequence MB> 291   template<capy::MutableBufferSequence MB>
HITCBC 291   10 [[nodiscard]] auto read_some(MB const& buffers) 292   10 [[nodiscard]] auto read_some(MB const& buffers)
292   { 293   {
HITCBC 293   10 return native_read_awaitable<MB>(*this, buffers); 294   10 return native_read_awaitable<MB>(*this, buffers);
294   } 295   }
295   296  
296   /** Asynchronously write data to the socket. 297   /** Asynchronously write data to the socket.
297   298  
298   Calls the backend implementation directly, bypassing virtual 299   Calls the backend implementation directly, bypassing virtual
299   dispatch. Otherwise identical to @ref io_stream::write_some. 300   dispatch. Otherwise identical to @ref io_stream::write_some.
300   301  
301   @param buffers The buffer sequence to write from. 302   @param buffers The buffer sequence to write from.
302   303  
303   @return An awaitable yielding `(error_code, std::size_t)`. 304   @return An awaitable yielding `(error_code, std::size_t)`.
304   305  
305   This socket must outlive the returned awaitable. The memory 306   This socket must outlive the returned awaitable. The memory
306   referenced by @p buffers must remain valid until the operation 307   referenced by @p buffers must remain valid until the operation
307   completes. 308   completes.
308   */ 309   */
309   template<capy::ConstBufferSequence CB> 310   template<capy::ConstBufferSequence CB>
HITCBC 310   12 [[nodiscard]] auto write_some(CB const& buffers) 311   12 [[nodiscard]] auto write_some(CB const& buffers)
311   { 312   {
HITCBC 312   12 return native_write_awaitable<CB>(*this, buffers); 313   12 return native_write_awaitable<CB>(*this, buffers);
313   } 314   }
314   315  
315   /** Asynchronously connect to a remote endpoint. 316   /** Asynchronously connect to a remote endpoint.
316   317  
317   Calls the backend implementation directly, bypassing virtual 318   Calls the backend implementation directly, bypassing virtual
318   dispatch. Otherwise identical to @ref tcp_socket::connect. 319   dispatch. Otherwise identical to @ref tcp_socket::connect.
319   320  
320   If the socket is not open, it is opened automatically using 321   If the socket is not open, it is opened automatically using
321   the protocol matching the endpoint's address family. An open 322   the protocol matching the endpoint's address family. An open
322   failure surfaces through the connect completion. 323   failure surfaces through the connect completion.
323   324  
324   @param ep The remote endpoint to connect to. 325   @param ep The remote endpoint to connect to.
325   326  
326   @return An awaitable yielding `io_result<>`. 327   @return An awaitable yielding `io_result<>`.
327   328  
328   This socket must outlive the returned awaitable. 329   This socket must outlive the returned awaitable.
329   */ 330   */
HITCBC 330   21 [[nodiscard]] auto connect(endpoint ep) 331   21 [[nodiscard]] auto connect(endpoint ep)
331   { 332   {
HITCBC 332   21 native_connect_awaitable aw(*this, ep); 333   21 native_connect_awaitable aw(*this, ep);
HITCBC 333   21 if (!is_open()) 334   21 if (!is_open())
HITCBC 334   2 aw.ec_ = open(ep.is_v6() ? tcp::v6() : tcp::v4()); 335   2 aw.ec_ = open(ep.is_v6() ? tcp::v6() : tcp::v4());
HITCBC 335   21 return aw; 336   21 return aw;
336   } 337   }
337   338  
338   /** Asynchronously wait for the socket to be ready. 339   /** Asynchronously wait for the socket to be ready.
339   340  
340   Calls the backend implementation directly, bypassing virtual 341   Calls the backend implementation directly, bypassing virtual
341   dispatch. Otherwise identical to @ref tcp_socket::wait. 342   dispatch. Otherwise identical to @ref tcp_socket::wait.
342   343  
343   @param w The wait direction (read, write, or error). 344   @param w The wait direction (read, write, or error).
344   345  
345   @return An awaitable yielding `io_result<>`. 346   @return An awaitable yielding `io_result<>`.
346   */ 347   */
HITCBC 347   6 [[nodiscard]] auto wait(wait_type w) 348   6 [[nodiscard]] auto wait(wait_type w)
348   { 349   {
HITCBC 349   6 return native_wait_awaitable(*this, w); 350   6 return native_wait_awaitable(*this, w);
350   } 351   }
351   }; 352   };
352   353  
353   } // namespace boost::corosio 354   } // namespace boost::corosio
354   355  
355   #endif 356   #endif