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