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