100.00% Lines (73/73) 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_IO_URING 34 + #if BOOST_COROSIO_HAS_URING
35 - #include <boost/corosio/native/detail/io_uring/io_uring_types.hpp> 35 + #include <boost/corosio/native/detail/uring/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 - 18 return self_.get_impl().wait( 188 + 6 return self_.get_impl().wait(h, env->executor, w_, token_, &ec_);
DCB 189 - 18 h, env->executor, w_, token_, &ec_);  
190   } 189   }
191   }; 190   };
192   191  
193   struct native_connect_awaitable 192   struct native_connect_awaitable
194   { 193   {
195   native_tcp_socket& self_; 194   native_tcp_socket& self_;
196   endpoint endpoint_; 195   endpoint endpoint_;
197   std::stop_token token_; 196   std::stop_token token_;
198   mutable std::error_code ec_; 197   mutable std::error_code ec_;
199   198  
HITCBC 200   21 native_connect_awaitable(native_tcp_socket& self, endpoint ep) noexcept 199   21 native_connect_awaitable(native_tcp_socket& self, endpoint ep) noexcept
HITCBC 201   21 : self_(self) 200   21 : self_(self)
HITCBC 202   21 , endpoint_(ep) 201   21 , endpoint_(ep)
203   { 202   {
HITCBC 204   21 } 203   21 }
205   204  
HITCBC 206   21 bool await_ready() const noexcept 205   21 bool await_ready() const noexcept
207   { 206   {
208   // A pre-set ec_ means the initiator failed before 207   // A pre-set ec_ means the initiator failed before
209   // dispatch (e.g. a closed object). 208   // dispatch (e.g. a closed object).
HITCBC 210   21 return static_cast<bool>(ec_) || token_.stop_requested(); 209   21 return static_cast<bool>(ec_) || token_.stop_requested();
211   } 210   }
212   211  
HITCBC 213   21 [[nodiscard]] capy::io_result<> await_resume() const noexcept 212   21 [[nodiscard]] capy::io_result<> await_resume() const noexcept
214   { 213   {
HITCBC 215   21 if (token_.stop_requested()) 214   21 if (token_.stop_requested())
HITCBC 216   2 return {make_error_code(std::errc::operation_canceled)}; 215   2 return {make_error_code(std::errc::operation_canceled)};
HITCBC 217   19 return {ec_}; 216   19 return {ec_};
218   } 217   }
219   218  
HITCBC 220   21 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 219   21 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
221   -> std::coroutine_handle<> 220   -> std::coroutine_handle<>
222   { 221   {
HITCBC 223   21 token_ = env->stop_token; 222   21 token_ = env->stop_token;
HITCBC 224   63 return self_.get_impl().connect( 223   63 return self_.get_impl().connect(
HITCBC 225   63 h, env->executor, endpoint_, token_, &ec_); 224   63 h, env->executor, endpoint_, token_, &ec_);
226   } 225   }
227   }; 226   };
228   227  
229   public: 228   public:
230   /** Construct a native socket from an execution context. 229   /** Construct a native socket from an execution context.
231   230  
232   @param ctx The execution context that will own this socket. 231   @param ctx The execution context that will own this socket.
233   */ 232   */
HITCBC 234   45 explicit native_tcp_socket(capy::execution_context& ctx) 233   45 explicit native_tcp_socket(capy::execution_context& ctx)
HITCBC 235   45 : io_object(create_handle<service_type>(ctx)) 234   45 : io_object(create_handle<service_type>(ctx))
236   { 235   {
HITCBC 237   45 } 236   45 }
238   237  
239   /** Construct a native socket from an executor. 238   /** Construct a native socket from an executor.
240   239  
241   @param ex The executor whose context will own the socket. 240   @param ex The executor whose context will own the socket.
242   */ 241   */
243   template<class Ex> 242   template<class Ex>
244   requires(!std::same_as<std::remove_cvref_t<Ex>, native_tcp_socket>) && 243   requires(!std::same_as<std::remove_cvref_t<Ex>, native_tcp_socket>) &&
245   capy::Executor<Ex> 244   capy::Executor<Ex>
246   explicit native_tcp_socket(Ex const& ex) : native_tcp_socket(ex.context()) 245   explicit native_tcp_socket(Ex const& ex) : native_tcp_socket(ex.context())
247   { 246   {
248   } 247   }
249   248  
250   /** Move construct. 249   /** Move construct.
251   250  
252   @param other The socket to move from. 251   @param other The socket to move from.
253   252  
254   @pre No awaitables returned by @p other's methods exist. 253   @pre No awaitables returned by @p other's methods exist.
255   @pre @p other is not referenced as a peer in any outstanding 254   @pre @p other is not referenced as a peer in any outstanding
256   accept awaitable. 255   accept awaitable.
257   @pre The execution context associated with @p other must 256   @pre The execution context associated with @p other must
258   outlive this socket. 257   outlive this socket.
259   */ 258   */
HITCBC 260   24 native_tcp_socket(native_tcp_socket&&) noexcept = default; 259   24 native_tcp_socket(native_tcp_socket&&) noexcept = default;
261   260  
262   /** Move assign. 261   /** Move assign.
263   262  
264   @param other The socket to move from. 263   @param other The socket to move from.
265   264  
266   @pre No awaitables returned by either `*this` or @p other's 265   @pre No awaitables returned by either `*this` or @p other's
267   methods exist. 266   methods exist.
268   @pre Neither `*this` nor @p other is referenced as a peer in 267   @pre Neither `*this` nor @p other is referenced as a peer in
269   any outstanding accept awaitable. 268   any outstanding accept awaitable.
270   @pre The execution context associated with @p other must 269   @pre The execution context associated with @p other must
271   outlive this socket. 270   outlive this socket.
272   */ 271   */
HITCBC 273   3 native_tcp_socket& operator=(native_tcp_socket&&) noexcept = default; 272   3 native_tcp_socket& operator=(native_tcp_socket&&) noexcept = default;
274   273  
275   native_tcp_socket(native_tcp_socket const&) = delete; 274   native_tcp_socket(native_tcp_socket const&) = delete;
276   native_tcp_socket& operator=(native_tcp_socket const&) = delete; 275   native_tcp_socket& operator=(native_tcp_socket const&) = delete;
277   276  
278   /** Asynchronously read data from the socket. 277   /** Asynchronously read data from the socket.
279   278  
280   Calls the backend implementation directly, bypassing virtual 279   Calls the backend implementation directly, bypassing virtual
281   dispatch. Otherwise identical to @ref io_stream::read_some. 280   dispatch. Otherwise identical to @ref io_stream::read_some.
282   281  
283   @param buffers The buffer sequence to read into. 282   @param buffers The buffer sequence to read into.
284   283  
285   @return An awaitable yielding `(error_code, std::size_t)`. 284   @return An awaitable yielding `(error_code, std::size_t)`.
286   285  
287   This socket must outlive the returned awaitable. The memory 286   This socket must outlive the returned awaitable. The memory
288   referenced by @p buffers must remain valid until the operation 287   referenced by @p buffers must remain valid until the operation
289   completes. 288   completes.
290   */ 289   */
291   template<capy::MutableBufferSequence MB> 290   template<capy::MutableBufferSequence MB>
HITCBC 292   10 [[nodiscard]] auto read_some(MB const& buffers) 291   10 [[nodiscard]] auto read_some(MB const& buffers)
293   { 292   {
HITCBC 294   10 return native_read_awaitable<MB>(*this, buffers); 293   10 return native_read_awaitable<MB>(*this, buffers);
295   } 294   }
296   295  
297   /** Asynchronously write data to the socket. 296   /** Asynchronously write data to the socket.
298   297  
299   Calls the backend implementation directly, bypassing virtual 298   Calls the backend implementation directly, bypassing virtual
300   dispatch. Otherwise identical to @ref io_stream::write_some. 299   dispatch. Otherwise identical to @ref io_stream::write_some.
301   300  
302   @param buffers The buffer sequence to write from. 301   @param buffers The buffer sequence to write from.
303   302  
304   @return An awaitable yielding `(error_code, std::size_t)`. 303   @return An awaitable yielding `(error_code, std::size_t)`.
305   304  
306   This socket must outlive the returned awaitable. The memory 305   This socket must outlive the returned awaitable. The memory
307   referenced by @p buffers must remain valid until the operation 306   referenced by @p buffers must remain valid until the operation
308   completes. 307   completes.
309   */ 308   */
310   template<capy::ConstBufferSequence CB> 309   template<capy::ConstBufferSequence CB>
HITCBC 311   12 [[nodiscard]] auto write_some(CB const& buffers) 310   12 [[nodiscard]] auto write_some(CB const& buffers)
312   { 311   {
HITCBC 313   12 return native_write_awaitable<CB>(*this, buffers); 312   12 return native_write_awaitable<CB>(*this, buffers);
314   } 313   }
315   314  
316   /** Asynchronously connect to a remote endpoint. 315   /** Asynchronously connect to a remote endpoint.
317   316  
318   Calls the backend implementation directly, bypassing virtual 317   Calls the backend implementation directly, bypassing virtual
319   dispatch. Otherwise identical to @ref tcp_socket::connect. 318   dispatch. Otherwise identical to @ref tcp_socket::connect.
320   319  
321   If the socket is not open, it is opened automatically using 320   If the socket is not open, it is opened automatically using
322   the protocol matching the endpoint's address family. An open 321   the protocol matching the endpoint's address family. An open
323   failure surfaces through the connect completion. 322   failure surfaces through the connect completion.
324   323  
325   @param ep The remote endpoint to connect to. 324   @param ep The remote endpoint to connect to.
326   325  
327   @return An awaitable yielding `io_result<>`. 326   @return An awaitable yielding `io_result<>`.
328   327  
329   This socket must outlive the returned awaitable. 328   This socket must outlive the returned awaitable.
330   */ 329   */
HITCBC 331   21 [[nodiscard]] auto connect(endpoint ep) 330   21 [[nodiscard]] auto connect(endpoint ep)
332   { 331   {
HITCBC 333   21 native_connect_awaitable aw(*this, ep); 332   21 native_connect_awaitable aw(*this, ep);
HITCBC 334   21 if (!is_open()) 333   21 if (!is_open())
HITCBC 335   2 aw.ec_ = open(ep.is_v6() ? tcp::v6() : tcp::v4()); 334   2 aw.ec_ = open(ep.is_v6() ? tcp::v6() : tcp::v4());
HITCBC 336   21 return aw; 335   21 return aw;
337   } 336   }
338   337  
339   /** Asynchronously wait for the socket to be ready. 338   /** Asynchronously wait for the socket to be ready.
340   339  
341   Calls the backend implementation directly, bypassing virtual 340   Calls the backend implementation directly, bypassing virtual
342   dispatch. Otherwise identical to @ref tcp_socket::wait. 341   dispatch. Otherwise identical to @ref tcp_socket::wait.
343   342  
344   @param w The wait direction (read, write, or error). 343   @param w The wait direction (read, write, or error).
345   344  
346   @return An awaitable yielding `io_result<>`. 345   @return An awaitable yielding `io_result<>`.
347   */ 346   */
HITCBC 348   6 [[nodiscard]] auto wait(wait_type w) 347   6 [[nodiscard]] auto wait(wait_type w)
349   { 348   {
HITCBC 350   6 return native_wait_awaitable(*this, w); 349   6 return native_wait_awaitable(*this, w);
351   } 350   }
352   }; 351   };
353   352  
354   } // namespace boost::corosio 353   } // namespace boost::corosio
355   354  
356   #endif 355   #endif