90.48% Lines (38/42) 100.00% Functions (13/13)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com) 2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3   // Copyright (c) 2026 Steve Gerbino 3   // Copyright (c) 2026 Steve Gerbino
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_TCP_SOCKET_HPP 11   #ifndef BOOST_COROSIO_TCP_SOCKET_HPP
12   #define BOOST_COROSIO_TCP_SOCKET_HPP 12   #define BOOST_COROSIO_TCP_SOCKET_HPP
13   13  
14   #include <boost/corosio/detail/config.hpp> 14   #include <boost/corosio/detail/config.hpp>
15   #include <boost/corosio/detail/platform.hpp> 15   #include <boost/corosio/detail/platform.hpp>
16   #include <boost/corosio/detail/except.hpp> 16   #include <boost/corosio/detail/except.hpp>
17   #include <boost/corosio/detail/native_handle.hpp> 17   #include <boost/corosio/detail/native_handle.hpp>
18   #include <boost/corosio/detail/op_base.hpp> 18   #include <boost/corosio/detail/op_base.hpp>
19   #include <boost/corosio/io/io_stream.hpp> 19   #include <boost/corosio/io/io_stream.hpp>
20   #include <boost/capy/io_result.hpp> 20   #include <boost/capy/io_result.hpp>
21   #include <boost/corosio/detail/buffer_param.hpp> 21   #include <boost/corosio/detail/buffer_param.hpp>
22   #include <boost/corosio/endpoint.hpp> 22   #include <boost/corosio/endpoint.hpp>
23   #include <boost/corosio/shutdown_type.hpp> 23   #include <boost/corosio/shutdown_type.hpp>
24   #include <boost/corosio/tcp.hpp> 24   #include <boost/corosio/tcp.hpp>
  25 + #include <boost/corosio/wait_type.hpp>
25   #include <boost/capy/ex/executor_ref.hpp> 26   #include <boost/capy/ex/executor_ref.hpp>
26   #include <boost/capy/ex/execution_context.hpp> 27   #include <boost/capy/ex/execution_context.hpp>
27   #include <boost/capy/ex/io_env.hpp> 28   #include <boost/capy/ex/io_env.hpp>
28   #include <boost/capy/concept/executor.hpp> 29   #include <boost/capy/concept/executor.hpp>
29   30  
30   #include <system_error> 31   #include <system_error>
31   32  
32   #include <concepts> 33   #include <concepts>
33   #include <coroutine> 34   #include <coroutine>
34   #include <cstddef> 35   #include <cstddef>
35   #include <stop_token> 36   #include <stop_token>
36   #include <type_traits> 37   #include <type_traits>
37   38  
38   namespace boost::corosio { 39   namespace boost::corosio {
39   40  
40   /** An asynchronous TCP socket for coroutine I/O. 41   /** An asynchronous TCP socket for coroutine I/O.
41   42  
42   This class provides asynchronous TCP socket operations that return 43   This class provides asynchronous TCP socket operations that return
43   awaitable types. Each operation participates in the affine awaitable 44   awaitable types. Each operation participates in the affine awaitable
44   protocol, ensuring coroutines resume on the correct executor. 45   protocol, ensuring coroutines resume on the correct executor.
45   46  
46   The socket must be opened before performing I/O operations. Operations 47   The socket must be opened before performing I/O operations. Operations
47   support cancellation through `std::stop_token` via the affine protocol, 48   support cancellation through `std::stop_token` via the affine protocol,
48   or explicitly through the `cancel()` member function. 49   or explicitly through the `cancel()` member function.
49   50  
50   @par Thread Safety 51   @par Thread Safety
51   Distinct objects: Safe.@n 52   Distinct objects: Safe.@n
52   Shared objects: Unsafe. A socket must not have concurrent operations 53   Shared objects: Unsafe. A socket must not have concurrent operations
53   of the same type (e.g., two simultaneous reads). One read and one 54   of the same type (e.g., two simultaneous reads). One read and one
54   write may be in flight simultaneously. 55   write may be in flight simultaneously.
55   56  
56   @par Semantics 57   @par Semantics
57   Wraps the platform TCP/IP stack. Operations dispatch to 58   Wraps the platform TCP/IP stack. Operations dispatch to
58   OS socket APIs via the io_context reactor (epoll, IOCP, 59   OS socket APIs via the io_context reactor (epoll, IOCP,
59   kqueue). Satisfies @ref capy::Stream. 60   kqueue). Satisfies @ref capy::Stream.
60   61  
61   @par Example 62   @par Example
62   @code 63   @code
63   io_context ioc; 64   io_context ioc;
64   tcp_socket s(ioc); 65   tcp_socket s(ioc);
65   s.open(); 66   s.open();
66   67  
67   // Using structured bindings 68   // Using structured bindings
68   auto [ec] = co_await s.connect( 69   auto [ec] = co_await s.connect(
69   endpoint(ipv4_address::loopback(), 8080)); 70   endpoint(ipv4_address::loopback(), 8080));
70   if (ec) 71   if (ec)
71   co_return; 72   co_return;
72   73  
73   char buf[1024]; 74   char buf[1024];
74   auto [read_ec, n] = co_await s.read_some( 75   auto [read_ec, n] = co_await s.read_some(
75   capy::mutable_buffer(buf, sizeof(buf))); 76   capy::mutable_buffer(buf, sizeof(buf)));
76   @endcode 77   @endcode
77   */ 78   */
78   class BOOST_COROSIO_DECL tcp_socket : public io_stream 79   class BOOST_COROSIO_DECL tcp_socket : public io_stream
79   { 80   {
80   public: 81   public:
81   /// The endpoint type used by this socket. 82   /// The endpoint type used by this socket.
82   using endpoint_type = corosio::endpoint; 83   using endpoint_type = corosio::endpoint;
83   84  
84   using shutdown_type = corosio::shutdown_type; 85   using shutdown_type = corosio::shutdown_type;
85   using enum corosio::shutdown_type; 86   using enum corosio::shutdown_type;
86   87  
87   /** Define backend hooks for TCP socket operations. 88   /** Define backend hooks for TCP socket operations.
88   89  
89   Platform backends (epoll, IOCP, kqueue, select) derive from 90   Platform backends (epoll, IOCP, kqueue, select) derive from
90   this to implement socket I/O, connection, and option management. 91   this to implement socket I/O, connection, and option management.
91   */ 92   */
92   struct implementation : io_stream::implementation 93   struct implementation : io_stream::implementation
93   { 94   {
94   /** Initiate an asynchronous connect to the given endpoint. 95   /** Initiate an asynchronous connect to the given endpoint.
95   96  
96   @param h Coroutine handle to resume on completion. 97   @param h Coroutine handle to resume on completion.
97   @param ex Executor for dispatching the completion. 98   @param ex Executor for dispatching the completion.
98   @param ep The remote endpoint to connect to. 99   @param ep The remote endpoint to connect to.
99   @param token Stop token for cancellation. 100   @param token Stop token for cancellation.
100   @param ec Output error code. 101   @param ec Output error code.
101   102  
102   @return Coroutine handle to resume immediately. 103   @return Coroutine handle to resume immediately.
103   */ 104   */
104   virtual std::coroutine_handle<> connect( 105   virtual std::coroutine_handle<> connect(
105   std::coroutine_handle<> h, 106   std::coroutine_handle<> h,
106   capy::executor_ref ex, 107   capy::executor_ref ex,
107   endpoint ep, 108   endpoint ep,
108   std::stop_token token, 109   std::stop_token token,
109   std::error_code* ec) = 0; 110   std::error_code* ec) = 0;
110   111  
  112 + /** Initiate an asynchronous wait for socket readiness.
  113 +
  114 + Completes when the socket becomes ready for the
  115 + specified direction, or an error condition is
  116 + reported. No bytes are transferred.
  117 +
  118 + @param h Coroutine handle to resume on completion.
  119 + @param ex Executor for dispatching the completion.
  120 + @param w The direction to wait on.
  121 + @param token Stop token for cancellation.
  122 + @param ec Output error code.
  123 +
  124 + @return Coroutine handle to resume immediately.
  125 + */
  126 + virtual std::coroutine_handle<> wait(
  127 + std::coroutine_handle<> h,
  128 + capy::executor_ref ex,
  129 + wait_type w,
  130 + std::stop_token token,
  131 + std::error_code* ec) = 0;
  132 +
111   /** Shut down the socket for the given direction(s). 133   /** Shut down the socket for the given direction(s).
112   134  
113   @param what The shutdown direction. 135   @param what The shutdown direction.
114   136  
115   @return Error code on failure, empty on success. 137   @return Error code on failure, empty on success.
116   */ 138   */
117   virtual std::error_code shutdown(shutdown_type what) noexcept = 0; 139   virtual std::error_code shutdown(shutdown_type what) noexcept = 0;
118   140  
119   /// Return the platform socket descriptor. 141   /// Return the platform socket descriptor.
120   virtual native_handle_type native_handle() const noexcept = 0; 142   virtual native_handle_type native_handle() const noexcept = 0;
121   143  
122   /** Request cancellation of pending asynchronous operations. 144   /** Request cancellation of pending asynchronous operations.
123   145  
124   All outstanding operations complete with operation_canceled error. 146   All outstanding operations complete with operation_canceled error.
125   Check `ec == cond::canceled` for portable comparison. 147   Check `ec == cond::canceled` for portable comparison.
126   */ 148   */
127   virtual void cancel() noexcept = 0; 149   virtual void cancel() noexcept = 0;
128   150  
129   /** Set a socket option. 151   /** Set a socket option.
130   152  
131   @param level The protocol level (e.g. `SOL_SOCKET`). 153   @param level The protocol level (e.g. `SOL_SOCKET`).
132   @param optname The option name (e.g. `SO_KEEPALIVE`). 154   @param optname The option name (e.g. `SO_KEEPALIVE`).
133   @param data Pointer to the option value. 155   @param data Pointer to the option value.
134   @param size Size of the option value in bytes. 156   @param size Size of the option value in bytes.
135   @return Error code on failure, empty on success. 157   @return Error code on failure, empty on success.
136   */ 158   */
137   virtual std::error_code set_option( 159   virtual std::error_code set_option(
138   int level, 160   int level,
139   int optname, 161   int optname,
140   void const* data, 162   void const* data,
141   std::size_t size) noexcept = 0; 163   std::size_t size) noexcept = 0;
142   164  
143   /** Get a socket option. 165   /** Get a socket option.
144   166  
145   @param level The protocol level (e.g. `SOL_SOCKET`). 167   @param level The protocol level (e.g. `SOL_SOCKET`).
146   @param optname The option name (e.g. `SO_KEEPALIVE`). 168   @param optname The option name (e.g. `SO_KEEPALIVE`).
147   @param data Pointer to receive the option value. 169   @param data Pointer to receive the option value.
148   @param size On entry, the size of the buffer. On exit, 170   @param size On entry, the size of the buffer. On exit,
149   the size of the option value. 171   the size of the option value.
150   @return Error code on failure, empty on success. 172   @return Error code on failure, empty on success.
151   */ 173   */
152   virtual std::error_code 174   virtual std::error_code
153   get_option(int level, int optname, void* data, std::size_t* size) 175   get_option(int level, int optname, void* data, std::size_t* size)
154   const noexcept = 0; 176   const noexcept = 0;
155   177  
156   /// Return the cached local endpoint. 178   /// Return the cached local endpoint.
157   virtual endpoint local_endpoint() const noexcept = 0; 179   virtual endpoint local_endpoint() const noexcept = 0;
158   180  
159   /// Return the cached remote endpoint. 181   /// Return the cached remote endpoint.
160   virtual endpoint remote_endpoint() const noexcept = 0; 182   virtual endpoint remote_endpoint() const noexcept = 0;
161   }; 183   };
162   184  
163   /// Represent the awaitable returned by @ref connect. 185   /// Represent the awaitable returned by @ref connect.
164   struct connect_awaitable 186   struct connect_awaitable
165   : detail::void_op_base<connect_awaitable> 187   : detail::void_op_base<connect_awaitable>
166   { 188   {
167   tcp_socket& s_; 189   tcp_socket& s_;
168   endpoint endpoint_; 190   endpoint endpoint_;
169   191  
HITCBC 170   8728 connect_awaitable(tcp_socket& s, endpoint ep) noexcept 192   8419 connect_awaitable(tcp_socket& s, endpoint ep) noexcept
HITCBC 171   8728 : s_(s), endpoint_(ep) {} 193   8419 : s_(s), endpoint_(ep) {}
172   194  
HITCBC 173   8728 std::coroutine_handle<> dispatch( 195   8419 std::coroutine_handle<> dispatch(
174   std::coroutine_handle<> h, capy::executor_ref ex) const 196   std::coroutine_handle<> h, capy::executor_ref ex) const
175   { 197   {
HITCBC 176   8728 return s_.get().connect(h, ex, endpoint_, token_, &ec_); 198   8419 return s_.get().connect(h, ex, endpoint_, token_, &ec_);
177   } 199   }
178   }; 200   };
179   201  
  202 + /// Represent the awaitable returned by @ref wait.
  203 + struct wait_awaitable
  204 + : detail::void_op_base<wait_awaitable>
  205 + {
  206 + tcp_socket& s_;
  207 + wait_type w_;
  208 +
HITGNC   209 + 6 wait_awaitable(tcp_socket& s, wait_type w) noexcept
HITGNC   210 + 6 : s_(s), w_(w) {}
  211 +
HITGNC   212 + 6 std::coroutine_handle<> dispatch(
  213 + std::coroutine_handle<> h, capy::executor_ref ex) const
  214 + {
HITGNC   215 + 6 return s_.get().wait(h, ex, w_, token_, &ec_);
  216 + }
  217 + };
  218 +
180   public: 219   public:
181   /** Destructor. 220   /** Destructor.
182   221  
183   Closes the socket if open, cancelling any pending operations. 222   Closes the socket if open, cancelling any pending operations.
184   */ 223   */
185   ~tcp_socket() override; 224   ~tcp_socket() override;
186   225  
187   /** Construct a socket from an execution context. 226   /** Construct a socket from an execution context.
188   227  
189   @param ctx The execution context that will own this socket. 228   @param ctx The execution context that will own this socket.
190   */ 229   */
191   explicit tcp_socket(capy::execution_context& ctx); 230   explicit tcp_socket(capy::execution_context& ctx);
192   231  
193   /** Construct a socket from an executor. 232   /** Construct a socket from an executor.
194   233  
195   The socket is associated with the executor's context. 234   The socket is associated with the executor's context.
196   235  
197   @param ex The executor whose context will own the socket. 236   @param ex The executor whose context will own the socket.
198   */ 237   */
199   template<class Ex> 238   template<class Ex>
200   requires(!std::same_as<std::remove_cvref_t<Ex>, tcp_socket>) && 239   requires(!std::same_as<std::remove_cvref_t<Ex>, tcp_socket>) &&
201   capy::Executor<Ex> 240   capy::Executor<Ex>
202   explicit tcp_socket(Ex const& ex) : tcp_socket(ex.context()) 241   explicit tcp_socket(Ex const& ex) : tcp_socket(ex.context())
203   { 242   {
204   } 243   }
205   244  
206   /** Move constructor. 245   /** Move constructor.
207   246  
208   Transfers ownership of the socket resources. 247   Transfers ownership of the socket resources.
209   248  
210   @param other The socket to move from. 249   @param other The socket to move from.
211   250  
212   @pre No awaitables returned by @p other's methods exist. 251   @pre No awaitables returned by @p other's methods exist.
213   @pre @p other is not referenced as a peer in any outstanding 252   @pre @p other is not referenced as a peer in any outstanding
214   accept awaitable. 253   accept awaitable.
215   @pre The execution context associated with @p other must 254   @pre The execution context associated with @p other must
216   outlive this socket. 255   outlive this socket.
217   */ 256   */
HITCBC 218   176 tcp_socket(tcp_socket&& other) noexcept : io_object(std::move(other)) {} 257   188 tcp_socket(tcp_socket&& other) noexcept : io_object(std::move(other)) {}
219   258  
220   /** Move assignment operator. 259   /** Move assignment operator.
221   260  
222   Closes any existing socket and transfers ownership. 261   Closes any existing socket and transfers ownership.
223   262  
224   @param other The socket to move from. 263   @param other The socket to move from.
225   264  
226   @pre No awaitables returned by either `*this` or @p other's 265   @pre No awaitables returned by either `*this` or @p other's
227   methods exist. 266   methods exist.
228   @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
229   any outstanding accept awaitable. 268   any outstanding accept awaitable.
230   @pre The execution context associated with @p other must 269   @pre The execution context associated with @p other must
231   outlive this socket. 270   outlive this socket.
232   271  
233   @return Reference to this socket. 272   @return Reference to this socket.
234   */ 273   */
HITCBC 235   10 tcp_socket& operator=(tcp_socket&& other) noexcept 274   10 tcp_socket& operator=(tcp_socket&& other) noexcept
236   { 275   {
HITCBC 237   10 if (this != &other) 276   10 if (this != &other)
238   { 277   {
HITCBC 239   10 close(); 278   10 close();
HITCBC 240   10 h_ = std::move(other.h_); 279   10 h_ = std::move(other.h_);
241   } 280   }
HITCBC 242   10 return *this; 281   10 return *this;
243   } 282   }
244   283  
245   tcp_socket(tcp_socket const&) = delete; 284   tcp_socket(tcp_socket const&) = delete;
246   tcp_socket& operator=(tcp_socket const&) = delete; 285   tcp_socket& operator=(tcp_socket const&) = delete;
247   286  
248   /** Open the socket. 287   /** Open the socket.
249   288  
250   Creates a TCP socket and associates it with the platform 289   Creates a TCP socket and associates it with the platform
251   reactor (IOCP on Windows). Calling @ref connect on a closed 290   reactor (IOCP on Windows). Calling @ref connect on a closed
252   socket opens it automatically with the endpoint's address family, 291   socket opens it automatically with the endpoint's address family,
253   so explicit `open()` is only needed when socket options must be 292   so explicit `open()` is only needed when socket options must be
254   set before connecting. 293   set before connecting.
255   294  
256   @param proto The protocol (IPv4 or IPv6). Defaults to 295   @param proto The protocol (IPv4 or IPv6). Defaults to
257   `tcp::v4()`. 296   `tcp::v4()`.
258   297  
259   @throws std::system_error on failure. 298   @throws std::system_error on failure.
260   */ 299   */
261   void open(tcp proto = tcp::v4()); 300   void open(tcp proto = tcp::v4());
262   301  
263   /** Bind the socket to a local endpoint. 302   /** Bind the socket to a local endpoint.
264   303  
265   Associates the socket with a local address and port before 304   Associates the socket with a local address and port before
266   connecting. Useful for multi-homed hosts or source-port 305   connecting. Useful for multi-homed hosts or source-port
267   pinning. 306   pinning.
268   307  
269   @param ep The local endpoint to bind to. 308   @param ep The local endpoint to bind to.
270   309  
271   @return An error code indicating success or the reason for 310   @return An error code indicating success or the reason for
272   failure. 311   failure.
273   312  
274   @par Error Conditions 313   @par Error Conditions
275   @li `errc::address_in_use`: The endpoint is already in use. 314   @li `errc::address_in_use`: The endpoint is already in use.
276   @li `errc::address_not_available`: The address is not 315   @li `errc::address_not_available`: The address is not
277   available on any local interface. 316   available on any local interface.
278   @li `errc::permission_denied`: Insufficient privileges to 317   @li `errc::permission_denied`: Insufficient privileges to
279   bind to the endpoint (e.g., privileged port). 318   bind to the endpoint (e.g., privileged port).
280   319  
281   @throws std::logic_error if the socket is not open. 320   @throws std::logic_error if the socket is not open.
282   */ 321   */
283   [[nodiscard]] std::error_code bind(endpoint ep); 322   [[nodiscard]] std::error_code bind(endpoint ep);
284   323  
285   /** Close the socket. 324   /** Close the socket.
286   325  
287   Releases socket resources. Any pending operations complete 326   Releases socket resources. Any pending operations complete
288   with `errc::operation_canceled`. 327   with `errc::operation_canceled`.
289   */ 328   */
290   void close(); 329   void close();
291   330  
292   /** Check if the socket is open. 331   /** Check if the socket is open.
293   332  
294   @return `true` if the socket is open and ready for operations. 333   @return `true` if the socket is open and ready for operations.
295   */ 334   */
HITCBC 296   53220 bool is_open() const noexcept 335   51379 bool is_open() const noexcept
297   { 336   {
298   #if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS) 337   #if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS)
299   return h_ && get().native_handle() != ~native_handle_type(0); 338   return h_ && get().native_handle() != ~native_handle_type(0);
300   #else 339   #else
HITCBC 301   53220 return h_ && get().native_handle() >= 0; 340   51379 return h_ && get().native_handle() >= 0;
302   #endif 341   #endif
303   } 342   }
304   343  
305   /** Initiate an asynchronous connect operation. 344   /** Initiate an asynchronous connect operation.
306   345  
307   If the socket is not already open, it is opened automatically 346   If the socket is not already open, it is opened automatically
308   using the address family of @p ep (IPv4 or IPv6). If the socket 347   using the address family of @p ep (IPv4 or IPv6). If the socket
309   is already open, the existing file descriptor is used as-is. 348   is already open, the existing file descriptor is used as-is.
310   349  
311   The operation supports cancellation via `std::stop_token` through 350   The operation supports cancellation via `std::stop_token` through
312   the affine awaitable protocol. If the associated stop token is 351   the affine awaitable protocol. If the associated stop token is
313   triggered, the operation completes immediately with 352   triggered, the operation completes immediately with
314   `errc::operation_canceled`. 353   `errc::operation_canceled`.
315   354  
316   @param ep The remote endpoint to connect to. 355   @param ep The remote endpoint to connect to.
317   356  
318   @return An awaitable that completes with `io_result<>`. 357   @return An awaitable that completes with `io_result<>`.
319   Returns success (default error_code) on successful connection, 358   Returns success (default error_code) on successful connection,
320   or an error code on failure including: 359   or an error code on failure including:
321   - connection_refused: No server listening at endpoint 360   - connection_refused: No server listening at endpoint
322   - timed_out: Connection attempt timed out 361   - timed_out: Connection attempt timed out
323   - network_unreachable: No route to host 362   - network_unreachable: No route to host
324   - operation_canceled: Cancelled via stop_token or cancel(). 363   - operation_canceled: Cancelled via stop_token or cancel().
325   Check `ec == cond::canceled` for portable comparison. 364   Check `ec == cond::canceled` for portable comparison.
326   365  
327   @throws std::system_error if the socket needs to be opened 366   @throws std::system_error if the socket needs to be opened
328   and the open fails. 367   and the open fails.
329   368  
330   @par Preconditions 369   @par Preconditions
331   This socket must outlive the returned awaitable. 370   This socket must outlive the returned awaitable.
332   371  
333   @par Example 372   @par Example
334   @code 373   @code
335   // Socket opened automatically with correct address family: 374   // Socket opened automatically with correct address family:
336   auto [ec] = co_await s.connect(endpoint); 375   auto [ec] = co_await s.connect(endpoint);
337   if (ec) { ... } 376   if (ec) { ... }
338   @endcode 377   @endcode
339   */ 378   */
HITCBC 340   8728 auto connect(endpoint ep) 379   8419 auto connect(endpoint ep)
341   { 380   {
HITCBC 342   8728 if (!is_open()) 381   8419 if (!is_open())
HITCBC 343   38 open(ep.is_v6() ? tcp::v6() : tcp::v4()); 382   40 open(ep.is_v6() ? tcp::v6() : tcp::v4());
HITCBC 344   8728 return connect_awaitable(*this, ep); 383   8419 return connect_awaitable(*this, ep);
  384 + }
  385 +
  386 + /** Wait for the socket to become ready in a given direction.
  387 +
  388 + Suspends until the socket is ready for the requested
  389 + direction, or an error condition is reported. No bytes
  390 + are transferred — useful for integrating with C libraries
  391 + that own the I/O on a nonblocking fd and only need
  392 + readiness notification (e.g. libpq async, libssh).
  393 +
  394 + The operation supports cancellation via `std::stop_token`
  395 + through the affine awaitable protocol. If the associated
  396 + stop token is triggered, the operation completes
  397 + immediately with `errc::operation_canceled`.
  398 +
  399 + @param w The wait direction (read, write, or error).
  400 +
  401 + @return An awaitable that completes with `io_result<>`.
  402 + On success, no bytes have been consumed from the
  403 + stream; a subsequent `read_some` (for read waits)
  404 + returns the available data.
  405 +
  406 + @par Preconditions
  407 + The socket must be open. This socket must outlive the
  408 + returned awaitable.
  409 + */
HITGNC   410 + 6 [[nodiscard]] auto wait(wait_type w)
  411 + {
HITGNC   412 + 6 return wait_awaitable(*this, w);
345   } 413   }
346   414  
347   /** Cancel any pending asynchronous operations. 415   /** Cancel any pending asynchronous operations.
348   416  
349   All outstanding operations complete with `errc::operation_canceled`. 417   All outstanding operations complete with `errc::operation_canceled`.
350   Check `ec == cond::canceled` for portable comparison. 418   Check `ec == cond::canceled` for portable comparison.
351   */ 419   */
352   void cancel(); 420   void cancel();
353   421  
354   /** Get the native socket handle. 422   /** Get the native socket handle.
355   423  
356   Returns the underlying platform-specific socket descriptor. 424   Returns the underlying platform-specific socket descriptor.
357   On POSIX systems this is an `int` file descriptor. 425   On POSIX systems this is an `int` file descriptor.
358   On Windows this is a `SOCKET` handle. 426   On Windows this is a `SOCKET` handle.
359   427  
360   @return The native socket handle, or -1/INVALID_SOCKET if not open. 428   @return The native socket handle, or -1/INVALID_SOCKET if not open.
361   429  
362   @par Preconditions 430   @par Preconditions
363   None. May be called on closed sockets. 431   None. May be called on closed sockets.
364   */ 432   */
365   native_handle_type native_handle() const noexcept; 433   native_handle_type native_handle() const noexcept;
366   434  
367   /** Disable sends or receives on the socket. 435   /** Disable sends or receives on the socket.
368   436  
369   TCP connections are full-duplex: each direction (send and receive) 437   TCP connections are full-duplex: each direction (send and receive)
370   operates independently. This function allows you to close one or 438   operates independently. This function allows you to close one or
371   both directions without destroying the socket. 439   both directions without destroying the socket.
372   440  
373   @li @ref shutdown_send sends a TCP FIN packet to the peer, 441   @li @ref shutdown_send sends a TCP FIN packet to the peer,
374   signaling that you have no more data to send. You can still 442   signaling that you have no more data to send. You can still
375   receive data until the peer also closes their send direction. 443   receive data until the peer also closes their send direction.
376   This is the most common use case, typically called before 444   This is the most common use case, typically called before
377   close() to ensure graceful connection termination. 445   close() to ensure graceful connection termination.
378   446  
379   @li @ref shutdown_receive disables reading on the socket. This 447   @li @ref shutdown_receive disables reading on the socket. This
380   does NOT send anything to the peer - they are not informed 448   does NOT send anything to the peer - they are not informed
381   and may continue sending data. Subsequent reads will fail 449   and may continue sending data. Subsequent reads will fail
382   or return end-of-file. Incoming data may be discarded or 450   or return end-of-file. Incoming data may be discarded or
383   buffered depending on the operating system. 451   buffered depending on the operating system.
384   452  
385   @li @ref shutdown_both combines both effects: sends a FIN and 453   @li @ref shutdown_both combines both effects: sends a FIN and
386   disables reading. 454   disables reading.
387   455  
388   When the peer shuts down their send direction (sends a FIN), 456   When the peer shuts down their send direction (sends a FIN),
389   subsequent read operations will complete with `capy::cond::eof`. 457   subsequent read operations will complete with `capy::cond::eof`.
390   Use the portable condition test rather than comparing error 458   Use the portable condition test rather than comparing error
391   codes directly: 459   codes directly:
392   460  
393   @code 461   @code
394   auto [ec, n] = co_await sock.read_some(buffer); 462   auto [ec, n] = co_await sock.read_some(buffer);
395   if (ec == capy::cond::eof) 463   if (ec == capy::cond::eof)
396   { 464   {
397   // Peer closed their send direction 465   // Peer closed their send direction
398   } 466   }
399   @endcode 467   @endcode
400   468  
401   Any error from the underlying system call is silently discarded 469   Any error from the underlying system call is silently discarded
402   because it is unlikely to be helpful. 470   because it is unlikely to be helpful.
403   471  
404   @param what Determines what operations will no longer be allowed. 472   @param what Determines what operations will no longer be allowed.
405   */ 473   */
406   void shutdown(shutdown_type what); 474   void shutdown(shutdown_type what);
407   475  
408   /** Set a socket option. 476   /** Set a socket option.
409   477  
410   Applies a type-safe socket option to the underlying socket. 478   Applies a type-safe socket option to the underlying socket.
411   The option type encodes the protocol level and option name. 479   The option type encodes the protocol level and option name.
412   480  
413   @par Example 481   @par Example
414   @code 482   @code
415   sock.set_option( socket_option::no_delay( true ) ); 483   sock.set_option( socket_option::no_delay( true ) );
416   sock.set_option( socket_option::receive_buffer_size( 65536 ) ); 484   sock.set_option( socket_option::receive_buffer_size( 65536 ) );
417   @endcode 485   @endcode
418   486  
419   @param opt The option to set. 487   @param opt The option to set.
420   488  
421   @throws std::logic_error if the socket is not open. 489   @throws std::logic_error if the socket is not open.
422   @throws std::system_error on failure. 490   @throws std::system_error on failure.
423   */ 491   */
424   template<class Option> 492   template<class Option>
HITCBC 425   60 void set_option(Option const& opt) 493   72 void set_option(Option const& opt)
426   { 494   {
HITCBC 427   60 if (!is_open()) 495   72 if (!is_open())
MISUBC 428   detail::throw_logic_error("set_option: socket not open"); 496   detail::throw_logic_error("set_option: socket not open");
HITCBC 429   60 std::error_code ec = get().set_option( 497   72 std::error_code ec = get().set_option(
430   Option::level(), Option::name(), opt.data(), opt.size()); 498   Option::level(), Option::name(), opt.data(), opt.size());
HITCBC 431   60 if (ec) 499   72 if (ec)
MISUBC 432   detail::throw_system_error(ec, "tcp_socket::set_option"); 500   detail::throw_system_error(ec, "tcp_socket::set_option");
HITCBC 433   60 } 501   72 }
434   502  
435   /** Get a socket option. 503   /** Get a socket option.
436   504  
437   Retrieves the current value of a type-safe socket option. 505   Retrieves the current value of a type-safe socket option.
438   506  
439   @par Example 507   @par Example
440   @code 508   @code
441   auto nd = sock.get_option<socket_option::no_delay>(); 509   auto nd = sock.get_option<socket_option::no_delay>();
442   if ( nd.value() ) 510   if ( nd.value() )
443   // Nagle's algorithm is disabled 511   // Nagle's algorithm is disabled
444   @endcode 512   @endcode
445   513  
446   @return The current option value. 514   @return The current option value.
447   515  
448   @throws std::logic_error if the socket is not open. 516   @throws std::logic_error if the socket is not open.
449   @throws std::system_error on failure. 517   @throws std::system_error on failure.
450   */ 518   */
451   template<class Option> 519   template<class Option>
HITCBC 452   62 Option get_option() const 520   62 Option get_option() const
453   { 521   {
HITCBC 454   62 if (!is_open()) 522   62 if (!is_open())
MISUBC 455   detail::throw_logic_error("get_option: socket not open"); 523   detail::throw_logic_error("get_option: socket not open");
HITCBC 456   62 Option opt{}; 524   62 Option opt{};
HITCBC 457   62 std::size_t sz = opt.size(); 525   62 std::size_t sz = opt.size();
458   std::error_code ec = 526   std::error_code ec =
HITCBC 459   62 get().get_option(Option::level(), Option::name(), opt.data(), &sz); 527   62 get().get_option(Option::level(), Option::name(), opt.data(), &sz);
HITCBC 460   62 if (ec) 528   62 if (ec)
MISUBC 461   detail::throw_system_error(ec, "tcp_socket::get_option"); 529   detail::throw_system_error(ec, "tcp_socket::get_option");
HITCBC 462   62 opt.resize(sz); 530   62 opt.resize(sz);
HITCBC 463   62 return opt; 531   62 return opt;
464   } 532   }
465   533  
466   /** Get the local endpoint of the socket. 534   /** Get the local endpoint of the socket.
467   535  
468   Returns the local address and port to which the socket is bound. 536   Returns the local address and port to which the socket is bound.
469   For a connected socket, this is the local side of the connection. 537   For a connected socket, this is the local side of the connection.
470   The endpoint is cached when the connection is established. 538   The endpoint is cached when the connection is established.
471   539  
472   @return The local endpoint, or a default endpoint (0.0.0.0:0) if 540   @return The local endpoint, or a default endpoint (0.0.0.0:0) if
473   the socket is not connected. 541   the socket is not connected.
474   542  
475   @par Thread Safety 543   @par Thread Safety
476   The cached endpoint value is set during connect/accept completion 544   The cached endpoint value is set during connect/accept completion
477   and cleared during close(). This function may be called concurrently 545   and cleared during close(). This function may be called concurrently
478   with I/O operations, but must not be called concurrently with 546   with I/O operations, but must not be called concurrently with
479   connect(), accept(), or close(). 547   connect(), accept(), or close().
480   */ 548   */
481   endpoint local_endpoint() const noexcept; 549   endpoint local_endpoint() const noexcept;
482   550  
483   /** Get the remote endpoint of the socket. 551   /** Get the remote endpoint of the socket.
484   552  
485   Returns the remote address and port to which the socket is connected. 553   Returns the remote address and port to which the socket is connected.
486   The endpoint is cached when the connection is established. 554   The endpoint is cached when the connection is established.
487   555  
488   @return The remote endpoint, or a default endpoint (0.0.0.0:0) if 556   @return The remote endpoint, or a default endpoint (0.0.0.0:0) if
489   the socket is not connected. 557   the socket is not connected.
490   558  
491   @par Thread Safety 559   @par Thread Safety
492   The cached endpoint value is set during connect/accept completion 560   The cached endpoint value is set during connect/accept completion
493   and cleared during close(). This function may be called concurrently 561   and cleared during close(). This function may be called concurrently
494   with I/O operations, but must not be called concurrently with 562   with I/O operations, but must not be called concurrently with
495   connect(), accept(), or close(). 563   connect(), accept(), or close().
496   */ 564   */
497   endpoint remote_endpoint() const noexcept; 565   endpoint remote_endpoint() const noexcept;
498   566  
499   protected: 567   protected:
HITCBC 500   10 tcp_socket() noexcept = default; 568   10 tcp_socket() noexcept = default;
501   569  
502   explicit tcp_socket(handle h) noexcept : io_object(std::move(h)) {} 570   explicit tcp_socket(handle h) noexcept : io_object(std::move(h)) {}
503   571  
504   private: 572   private:
505   friend class tcp_acceptor; 573   friend class tcp_acceptor;
506   574  
507   /// Open the socket for the given protocol triple. 575   /// Open the socket for the given protocol triple.
508   void open_for_family(int family, int type, int protocol); 576   void open_for_family(int family, int type, int protocol);
509   577  
HITCBC 510   62157 inline implementation& get() const noexcept 578   60014 inline implementation& get() const noexcept
511   { 579   {
HITCBC 512   62157 return *static_cast<implementation*>(h_.get()); 580   60014 return *static_cast<implementation*>(h_.get());
513   } 581   }
514   }; 582   };
515   583  
516   } // namespace boost::corosio 584   } // namespace boost::corosio
517   585  
518   #endif 586   #endif