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