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