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