100.00% Lines (48/48) 100.00% Functions (13/13)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2026 Michael Vandeberg 2   // Copyright (c) 2026 Michael Vandeberg
3   // 3   //
4   // Distributed under the Boost Software License, Version 1.0. (See accompanying 4   // Distributed under the Boost Software License, Version 1.0. (See accompanying
5   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6   // 6   //
7   // Official repository: https://github.com/cppalliance/corosio 7   // Official repository: https://github.com/cppalliance/corosio
8   // 8   //
9   9  
10   #ifndef BOOST_COROSIO_LOCAL_STREAM_SOCKET_HPP 10   #ifndef BOOST_COROSIO_LOCAL_STREAM_SOCKET_HPP
11   #define BOOST_COROSIO_LOCAL_STREAM_SOCKET_HPP 11   #define BOOST_COROSIO_LOCAL_STREAM_SOCKET_HPP
12   12  
13   #include <boost/corosio/family.hpp> 13   #include <boost/corosio/family.hpp>
14   #include <boost/corosio/detail/config.hpp> 14   #include <boost/corosio/detail/config.hpp>
15   #include <boost/corosio/detail/platform.hpp> 15   #include <boost/corosio/detail/platform.hpp>
16   #include <boost/corosio/detail/native_handle.hpp> 16   #include <boost/corosio/detail/native_handle.hpp>
17   #include <boost/corosio/detail/op_base.hpp> 17   #include <boost/corosio/detail/op_base.hpp>
18   #include <boost/corosio/io/io_stream.hpp> 18   #include <boost/corosio/io/io_stream.hpp>
19   #include <boost/capy/results.hpp> 19   #include <boost/capy/results.hpp>
20   #include <boost/corosio/detail/buffer_param.hpp> 20   #include <boost/corosio/detail/buffer_param.hpp>
21   #include <boost/corosio/error.hpp> 21   #include <boost/corosio/error.hpp>
22   #include <boost/corosio/local_endpoint.hpp> 22   #include <boost/corosio/local_endpoint.hpp>
23   #include <boost/corosio/shutdown_type.hpp> 23   #include <boost/corosio/shutdown_type.hpp>
24   #include <boost/corosio/wait_type.hpp> 24   #include <boost/corosio/wait_type.hpp>
25   #include <boost/capy/ex/executor_ref.hpp> 25   #include <boost/capy/ex/executor_ref.hpp>
26   #include <boost/capy/ex/execution_context.hpp> 26   #include <boost/capy/ex/execution_context.hpp>
27   #include <boost/capy/ex/await_context.hpp> 27   #include <boost/capy/ex/await_context.hpp>
28   #include <boost/capy/concept/executor.hpp> 28   #include <boost/capy/concept/executor.hpp>
29   29  
30   #include <system_error> 30   #include <system_error>
31   31  
32   #include <concepts> 32   #include <concepts>
33   #include <boost/capy/continuation.hpp> 33   #include <boost/capy/continuation.hpp>
34   34  
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   #include <utility> 39   #include <utility>
40   40  
41   namespace boost::corosio { 41   namespace boost::corosio {
42   42  
43   /** Reads and writes a Unix domain stream, from a coroutine. 43   /** Reads and writes a Unix domain stream, from a coroutine.
44   44  
45   This class provides asynchronous Unix domain stream socket 45   This class provides asynchronous Unix domain stream socket
46   operations that return awaitable types. Each operation 46   operations that return awaitable types. Each operation
47   participates in the affine awaitable protocol, ensuring 47   participates in the affine awaitable protocol, ensuring
48   coroutines resume on the correct executor. 48   coroutines resume on the correct executor.
49   49  
50   The socket must be opened before performing I/O operations. 50   The socket must be opened before performing I/O operations.
51   Operations support cancellation through `std::stop_token` via 51   Operations support cancellation through `std::stop_token` via
52   the affine protocol, or explicitly through the `cancel()` 52   the affine protocol, or explicitly through the `cancel()`
53   member function. 53   member function.
54   54  
55   @par Thread Safety 55   @par Thread Safety
56   Distinct objects: Safe.@n 56   Distinct objects: Safe.@n
57   Shared objects: Unsafe. A socket must not have concurrent 57   Shared objects: Unsafe. A socket must not have concurrent
58   operations of the same type (e.g., two simultaneous reads). 58   operations of the same type (e.g., two simultaneous reads).
59   One read and one write may be in flight simultaneously. 59   One read and one write may be in flight simultaneously.
60   60  
61   @par Semantics 61   @par Semantics
62   Wraps the platform Unix domain socket stack. Operations 62   Wraps the platform Unix domain socket stack. Operations
63   dispatch to OS socket APIs via the `io_context` backend 63   dispatch to OS socket APIs via the `io_context` backend
64   (epoll, kqueue, select, or IOCP). Satisfies @ref capy::Stream. 64   (epoll, kqueue, select, or IOCP). Satisfies @ref capy::Stream.
65   65  
66   @par Example 66   @par Example
67   @par !example connect_and_read 67   @par !example connect_and_read
68   */ 68   */
69   class BOOST_COROSIO_DECL local_stream_socket : public io_stream 69   class BOOST_COROSIO_DECL local_stream_socket : public io_stream
70   { 70   {
71   public: 71   public:
72   /// The endpoint type used by this socket. 72   /// The endpoint type used by this socket.
73   using endpoint_type = corosio::local_endpoint; 73   using endpoint_type = corosio::local_endpoint;
74   74  
75   /// The shutdown direction type used by this socket. 75   /// The shutdown direction type used by this socket.
76   using shutdown_type = corosio::shutdown_type; 76   using shutdown_type = corosio::shutdown_type;
77   using enum corosio::shutdown_type; 77   using enum corosio::shutdown_type;
78   78  
79   /** Define backend hooks for local stream socket operations. 79   /** Define backend hooks for local stream socket operations.
80   80  
81   Platform backends (epoll, kqueue, select) derive from this 81   Platform backends (epoll, kqueue, select) derive from this
82   to implement socket I/O, connection, and option management. 82   to implement socket I/O, connection, and option management.
83   */ 83   */
84   struct implementation : io_stream::implementation 84   struct implementation : io_stream::implementation
85   { 85   {
86   /** Initiate an asynchronous connect to the given endpoint. 86   /** Initiate an asynchronous connect to the given endpoint.
87   87  
88   @param cont Continuation to resume on completion; it lives in 88   @param cont Continuation to resume on completion; it lives in
89   the awaiting frame until then. 89   the awaiting frame until then.
90   @param ex Executor for dispatching the completion. 90   @param ex Executor for dispatching the completion.
91   @param ep The local endpoint (path) to connect to. 91   @param ep The local endpoint (path) to connect to.
92   @param token Stop token for cancellation. 92   @param token Stop token for cancellation.
93   @param ec Output error code. 93   @param ec Output error code.
94   94  
95   @return Coroutine handle to resume immediately. 95   @return Coroutine handle to resume immediately.
96   */ 96   */
97   virtual std::coroutine_handle<> connect( 97   virtual std::coroutine_handle<> connect(
98   capy::continuation& cont, 98   capy::continuation& cont,
99   capy::executor_ref ex, 99   capy::executor_ref ex,
100   corosio::local_endpoint ep, 100   corosio::local_endpoint ep,
101   std::stop_token token, 101   std::stop_token token,
102   std::error_code* ec) = 0; 102   std::error_code* ec) = 0;
103   103  
104   /** Initiate an asynchronous wait for socket readiness. 104   /** Initiate an asynchronous wait for socket readiness.
105   105  
106   Completes when the socket becomes ready for the 106   Completes when the socket becomes ready for the
107   specified direction, or an error condition is 107   specified direction, or an error condition is
108   reported. No bytes are transferred. 108   reported. No bytes are transferred.
109   109  
110   @param cont Continuation to resume on completion; it lives in 110   @param cont Continuation to resume on completion; it lives in
111   the awaiting frame until then. 111   the awaiting frame until then.
112   @param ex Executor for dispatching the completion. 112   @param ex Executor for dispatching the completion.
113   @param w The direction to wait on. 113   @param w The direction to wait on.
114   @param token Stop token for cancellation. 114   @param token Stop token for cancellation.
115   @param ec Output error code. 115   @param ec Output error code.
116   116  
117   @return Coroutine handle to resume immediately. 117   @return Coroutine handle to resume immediately.
118   */ 118   */
119   virtual std::coroutine_handle<> wait( 119   virtual std::coroutine_handle<> wait(
120   capy::continuation& cont, 120   capy::continuation& cont,
121   capy::executor_ref ex, 121   capy::executor_ref ex,
122   wait_type w, 122   wait_type w,
123   std::stop_token token, 123   std::stop_token token,
124   std::error_code* ec) = 0; 124   std::error_code* ec) = 0;
125   125  
126   /** Shut down the socket for the given direction(s). 126   /** Shut down the socket for the given direction(s).
127   127  
128   @param what The shutdown direction. 128   @param what The shutdown direction.
129   129  
130   @return Error code on failure, empty on success. 130   @return Error code on failure, empty on success.
131   */ 131   */
132   virtual std::error_code shutdown(shutdown_type what) noexcept = 0; 132   virtual std::error_code shutdown(shutdown_type what) noexcept = 0;
133   133  
134   /// Return the platform socket descriptor. 134   /// Return the platform socket descriptor.
135   virtual native_handle_type native_handle() const noexcept = 0; 135   virtual native_handle_type native_handle() const noexcept = 0;
136   136  
137   /** Return the socket's address family. 137   /** Return the socket's address family.
138   138  
139   Local sockets have no IP family; implementations return 139   Local sockets have no IP family; implementations return
140   `v4`, which the family-neutral options applicable to them 140   `v4`, which the family-neutral options applicable to them
141   ignore. 141   ignore.
142   142  
143   @return The address family for option rendering. 143   @return The address family for option rendering.
144   */ 144   */
145   virtual corosio::family family() const noexcept = 0; 145   virtual corosio::family family() const noexcept = 0;
146   146  
147   /** Release ownership of the native socket handle. 147   /** Release ownership of the native socket handle.
148   148  
149   Deregisters the socket from the reactor without closing 149   Deregisters the socket from the reactor without closing
150   the descriptor. The caller takes ownership. 150   the descriptor. The caller takes ownership.
151   151  
152   @return The native handle. 152   @return The native handle.
153   */ 153   */
154   virtual native_handle_type release_socket() noexcept = 0; 154   virtual native_handle_type release_socket() noexcept = 0;
155   155  
156   /** Request cancellation of pending asynchronous operations. 156   /** Request cancellation of pending asynchronous operations.
157   157  
158   Operations still in flight complete with `operation_canceled`; an 158   Operations still in flight complete with `operation_canceled`; an
159   operation whose result is already decided reports that result. 159   operation whose result is already decided reports that result.
160   Check `ec == cond::canceled` for portable comparison. 160   Check `ec == cond::canceled` for portable comparison.
161   */ 161   */
162   virtual void cancel() noexcept = 0; 162   virtual void cancel() noexcept = 0;
163   163  
164   /** Set a socket option. 164   /** Set a socket option.
165   165  
166   @param level The protocol level (e.g. `SOL_SOCKET`). 166   @param level The protocol level (e.g. `SOL_SOCKET`).
167   @param optname The option name (e.g. `SO_KEEPALIVE`). 167   @param optname The option name (e.g. `SO_KEEPALIVE`).
168   @param data Pointer to the option value. 168   @param data Pointer to the option value.
169   @param size Size of the option value in bytes. 169   @param size Size of the option value in bytes.
170   @return Error code on failure, empty on success. 170   @return Error code on failure, empty on success.
171   */ 171   */
172   virtual std::error_code set_option( 172   virtual std::error_code set_option(
173   int level, 173   int level,
174   int optname, 174   int optname,
175   void const* data, 175   void const* data,
176   std::size_t size) noexcept = 0; 176   std::size_t size) noexcept = 0;
177   177  
178   /** Get a socket option. 178   /** Get a socket option.
179   179  
180   @param level The protocol level (e.g. `SOL_SOCKET`). 180   @param level The protocol level (e.g. `SOL_SOCKET`).
181   @param optname The option name (e.g. `SO_KEEPALIVE`). 181   @param optname The option name (e.g. `SO_KEEPALIVE`).
182   @param data Pointer to receive the option value. 182   @param data Pointer to receive the option value.
183   @param size On entry, the size of the buffer. On exit, 183   @param size On entry, the size of the buffer. On exit,
184   the size of the option value. 184   the size of the option value.
185   @return Error code on failure, empty on success. 185   @return Error code on failure, empty on success.
186   */ 186   */
187   virtual std::error_code 187   virtual std::error_code
188   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)
189   const noexcept = 0; 189   const noexcept = 0;
190   190  
191   /// Return the cached local endpoint. 191   /// Return the cached local endpoint.
192   virtual corosio::local_endpoint local_endpoint() const noexcept = 0; 192   virtual corosio::local_endpoint local_endpoint() const noexcept = 0;
193   193  
194   /// Return the cached remote endpoint. 194   /// Return the cached remote endpoint.
195   virtual corosio::local_endpoint remote_endpoint() const noexcept = 0; 195   virtual corosio::local_endpoint remote_endpoint() const noexcept = 0;
196   }; 196   };
197   197  
198   /// Represent the awaitable returned by @ref connect. 198   /// Represent the awaitable returned by @ref connect.
199   struct connect_awaitable : detail::void_op_base<connect_awaitable> 199   struct connect_awaitable : detail::void_op_base<connect_awaitable>
200   { 200   {
201   private: 201   private:
202   friend local_stream_socket; 202   friend local_stream_socket;
203   203  
HITCBC 204   27 connect_awaitable( 204   27 connect_awaitable(
205   local_stream_socket& s, corosio::local_endpoint ep) noexcept 205   local_stream_socket& s, corosio::local_endpoint ep) noexcept
HITCBC 206   54 : s_(s) 206   54 : s_(s)
HITCBC 207   27 , endpoint_(ep) 207   27 , endpoint_(ep)
208   { 208   {
HITCBC 209   27 } 209   27 }
210   210  
211   friend detail::void_op_base<connect_awaitable>; 211   friend detail::void_op_base<connect_awaitable>;
212   212  
213   local_stream_socket& s_; 213   local_stream_socket& s_;
214   corosio::local_endpoint endpoint_; 214   corosio::local_endpoint endpoint_;
215   215  
216   std::coroutine_handle<> 216   std::coroutine_handle<>
HITCBC 217   25 dispatch(capy::continuation& cont, capy::executor_ref ex) const 217   25 dispatch(capy::continuation& cont, capy::executor_ref ex) const
218   { 218   {
HITCBC 219   25 return s_.get().connect(cont, ex, endpoint_, token_, &ec_); 219   25 return s_.get().connect(cont, ex, endpoint_, token_, &ec_);
220   } 220   }
221   }; 221   };
222   222  
223   /// Represent the awaitable returned by @ref wait. 223   /// Represent the awaitable returned by @ref wait.
224   struct wait_awaitable : detail::void_op_base<wait_awaitable> 224   struct wait_awaitable : detail::void_op_base<wait_awaitable>
225   { 225   {
226   private: 226   private:
227   friend local_stream_socket; 227   friend local_stream_socket;
228   228  
HITCBC 229   16 wait_awaitable(local_stream_socket& s, wait_type w) noexcept 229   16 wait_awaitable(local_stream_socket& s, wait_type w) noexcept
HITCBC 230   32 : s_(s) 230   32 : s_(s)
HITCBC 231   16 , w_(w) 231   16 , w_(w)
232   { 232   {
HITCBC 233   16 } 233   16 }
234   234  
235   friend detail::void_op_base<wait_awaitable>; 235   friend detail::void_op_base<wait_awaitable>;
236   236  
237   local_stream_socket& s_; 237   local_stream_socket& s_;
238   wait_type w_; 238   wait_type w_;
239   239  
240   std::coroutine_handle<> 240   std::coroutine_handle<>
HITCBC 241   14 dispatch(capy::continuation& cont, capy::executor_ref ex) const 241   14 dispatch(capy::continuation& cont, capy::executor_ref ex) const
242   { 242   {
HITCBC 243   14 return s_.get().wait(cont, ex, w_, token_, &ec_); 243   14 return s_.get().wait(cont, ex, w_, token_, &ec_);
244   } 244   }
245   }; 245   };
246   246  
247   public: 247   public:
248   /** Destructor. 248   /** Destructor.
249   249  
250   Closes the socket if open, cancelling any pending operations. 250   Closes the socket if open, cancelling any pending operations.
251   */ 251   */
252   ~local_stream_socket() override; 252   ~local_stream_socket() override;
253   253  
254   /** Construct a socket from an execution context. 254   /** Construct a socket from an execution context.
255   255  
256   @param ctx The execution context that owns this socket. 256   @param ctx The execution context that owns this socket.
257   */ 257   */
258   explicit local_stream_socket(capy::execution_context& ctx); 258   explicit local_stream_socket(capy::execution_context& ctx);
259   259  
260   /** Construct a socket from an executor. 260   /** Construct a socket from an executor.
261   261  
262   The socket is associated with the executor's context. 262   The socket is associated with the executor's context.
263   263  
264   @tparam Ex A type satisfying capy::Executor. 264   @tparam Ex A type satisfying capy::Executor.
265   265  
266   @param ex The executor whose context owns the socket. 266   @param ex The executor whose context owns the socket.
267   */ 267   */
268   template<class Ex> 268   template<class Ex>
269   requires(!std::same_as<std::remove_cvref_t<Ex>, local_stream_socket>) && 269   requires(!std::same_as<std::remove_cvref_t<Ex>, local_stream_socket>) &&
270   capy::Executor<Ex> 270   capy::Executor<Ex>
271   explicit local_stream_socket(Ex const& ex) 271   explicit local_stream_socket(Ex const& ex)
272   : local_stream_socket(ex.context()) 272   : local_stream_socket(ex.context())
273   { 273   {
274   } 274   }
275   275  
276   /** Move constructor. 276   /** Move constructor.
277   277  
278   Transfers ownership of the socket resources. 278   Transfers ownership of the socket resources.
279   279  
280   @param other The socket to move from. 280   @param other The socket to move from.
281   281  
282   @pre No awaitables returned by @p other's methods exist. 282   @pre No awaitables returned by @p other's methods exist.
283   @pre The execution context associated with @p other must 283   @pre The execution context associated with @p other must
284   outlive this socket. 284   outlive this socket.
285   */ 285   */
HITCBC 286   16 local_stream_socket(local_stream_socket&& other) noexcept 286   16 local_stream_socket(local_stream_socket&& other) noexcept
HITCBC 287   16 : io_object(std::move(other)) 287   16 : io_object(std::move(other))
288   { 288   {
HITCBC 289   16 } 289   16 }
290   290  
291   /** Move assignment operator. 291   /** Move assignment operator.
292   292  
293   Closes any existing socket and transfers ownership. 293   Closes any existing socket and transfers ownership.
294   294  
295   @param other The socket to move from. 295   @param other The socket to move from.
296   296  
297   @pre No awaitables returned by either `*this` or @p other's 297   @pre No awaitables returned by either `*this` or @p other's
298   methods exist. 298   methods exist.
299   @pre The execution context associated with @p other must 299   @pre The execution context associated with @p other must
300   outlive this socket. 300   outlive this socket.
301   301  
302   @return Reference to this socket. 302   @return Reference to this socket.
303   */ 303   */
HITCBC 304   4 local_stream_socket& operator=(local_stream_socket&& other) noexcept 304   4 local_stream_socket& operator=(local_stream_socket&& other) noexcept
305   { 305   {
HITCBC 306   4 if (this != &other) 306   4 if (this != &other)
307   { 307   {
HITCBC 308   2 close(); 308   2 close();
HITCBC 309   2 io_object::operator=(std::move(other)); 309   2 io_object::operator=(std::move(other));
310   } 310   }
HITCBC 311   4 return *this; 311   4 return *this;
312   } 312   }
313   313  
314   /// Copy construction is disabled; the handle is uniquely owned. 314   /// Copy construction is disabled; the handle is uniquely owned.
315   local_stream_socket(local_stream_socket const&) = delete; 315   local_stream_socket(local_stream_socket const&) = delete;
316   /// Copy assignment is disabled; the handle is uniquely owned. 316   /// Copy assignment is disabled; the handle is uniquely owned.
317   local_stream_socket& operator=(local_stream_socket const&) = delete; 317   local_stream_socket& operator=(local_stream_socket const&) = delete;
318   318  
319   /** Open the socket. 319   /** Open the socket.
320   320  
321   Creates a Unix stream socket and associates it with 321   Creates a Unix stream socket and associates it with
322   the platform reactor. 322   the platform reactor.
323   323  
324   Failures such as descriptor exhaustion are normal runtime 324   Failures such as descriptor exhaustion are normal runtime
325   conditions and are reported through the returned error code. 325   conditions and are reported through the returned error code.
326   Opening an already-open socket is a no-op that reports 326   Opening an already-open socket is a no-op that reports
327   success. 327   success.
328   328  
329   329  
330   @return The error code, empty on success. 330   @return The error code, empty on success.
331   */ 331   */
332   [[nodiscard]] std::error_code open() noexcept; 332   [[nodiscard]] std::error_code open() noexcept;
333   333  
334   /** Close the socket. 334   /** Close the socket.
335   335  
336   Releases socket resources. Any pending operations complete 336   Releases socket resources. Any pending operations complete
337   with `errc::operation_canceled`. 337   with `errc::operation_canceled`.
338   */ 338   */
339   void close() noexcept; 339   void close() noexcept;
340   340  
341   /** Check if the socket is open. 341   /** Check if the socket is open.
342   342  
343   @return `true` if the socket is open and ready for operations. 343   @return `true` if the socket is open and ready for operations.
344   */ 344   */
HITCBC 345   1182 bool is_open() const noexcept 345   1182 bool is_open() const noexcept
346   { 346   {
347   #if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS) 347   #if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS)
348   return h_ && get().native_handle() != ~native_handle_type(0); 348   return h_ && get().native_handle() != ~native_handle_type(0);
349   #else 349   #else
HITCBC 350   1182 return h_ && get().native_handle() >= 0; 350   1182 return h_ && get().native_handle() >= 0;
351   #endif 351   #endif
352   } 352   }
353   353  
354   /** Initiate an asynchronous connect operation. 354   /** Initiate an asynchronous connect operation.
355   355  
356   If the socket is not already open, it is opened automatically. 356   If the socket is not already open, it is opened automatically.
357   357  
358   @param ep The local endpoint (path) to connect to. 358   @param ep The local endpoint (path) to connect to.
359   359  
360   @return An awaitable that completes with `results<>`. 360   @return An awaitable that completes with `results<>`.
361   361  
362   If the socket needs to be opened and the open fails, the 362   If the socket needs to be opened and the open fails, the
363   awaitable completes immediately with that error. 363   awaitable completes immediately with that error.
364   */ 364   */
HITCBC 365   27 [[nodiscard]] auto connect(corosio::local_endpoint ep) 365   27 [[nodiscard]] auto connect(corosio::local_endpoint ep)
366   { 366   {
HITCBC 367   27 connect_awaitable aw(*this, ep); 367   27 connect_awaitable aw(*this, ep);
HITCBC 368   27 if (!is_open()) 368   27 if (!is_open())
HITCBC 369   19 aw.ec_ = open(); 369   19 aw.ec_ = open();
HITCBC 370   27 return aw; 370   27 return aw;
371   } 371   }
372   372  
373   /** Wait for the socket to become ready in a given direction. 373   /** Wait for the socket to become ready in a given direction.
374   374  
375   Suspends until the socket is ready for the requested 375   Suspends until the socket is ready for the requested
376   direction, or an error condition is reported. No bytes 376   direction, or an error condition is reported. No bytes
377   are transferred. 377   are transferred.
378   378  
379   @param w The wait direction (read, write, or error). 379   @param w The wait direction (read, write, or error).
380   380  
381   @return An awaitable that completes with `results<>`. 381   @return An awaitable that completes with `results<>`.
382   382  
383   A closed socket completes with `errc::bad_file_descriptor`. 383   A closed socket completes with `errc::bad_file_descriptor`.
384   384  
385   @pre This socket must outlive the returned awaitable. 385   @pre This socket must outlive the returned awaitable.
386   */ 386   */
HITCBC 387   16 [[nodiscard]] auto wait(wait_type w) 387   16 [[nodiscard]] auto wait(wait_type w)
388   { 388   {
HITCBC 389   16 return wait_awaitable(*this, w); 389   16 return wait_awaitable(*this, w);
390   } 390   }
391   391  
392   /** Cancel any pending asynchronous operations. 392   /** Cancel any pending asynchronous operations.
393   393  
394   Operations still in flight complete with `errc::operation_canceled`; 394   Operations still in flight complete with `errc::operation_canceled`;
395   an operation whose result is already decided reports that result. 395   an operation whose result is already decided reports that result.
396   Check `ec == cond::canceled` for portable comparison. 396   Check `ec == cond::canceled` for portable comparison.
397   */ 397   */
398   void cancel() noexcept; 398   void cancel() noexcept;
399   399  
400   /** Get the native socket handle. 400   /** Get the native socket handle.
401   401  
402   Returns the underlying platform-specific socket descriptor. 402   Returns the underlying platform-specific socket descriptor.
403   On POSIX systems this is an `int` file descriptor. 403   On POSIX systems this is an `int` file descriptor.
404   404  
405   @return The native socket handle, or an invalid sentinel 405   @return The native socket handle, or an invalid sentinel
406   if not open. 406   if not open.
407   */ 407   */
408   native_handle_type native_handle() const noexcept; 408   native_handle_type native_handle() const noexcept;
409   409  
410   /** Query the number of bytes available for reading. 410   /** Query the number of bytes available for reading.
411   411  
412   A closed socket reports `errc::bad_file_descriptor`. Other 412   A closed socket reports `errc::bad_file_descriptor`. Other
413   failures carry the error code from the operating system. On 413   failures carry the error code from the operating system. On
414   failure the count is zero. 414   failure the count is zero.
415   415  
416   @return The error code and the number of readable bytes. 416   @return The error code and the number of readable bytes.
417   */ 417   */
418   [[nodiscard]] capy::results<std::size_t> available() const noexcept; 418   [[nodiscard]] capy::results<std::size_t> available() const noexcept;
419   419  
420   /** Release ownership of the native socket handle. 420   /** Release ownership of the native socket handle.
421   421  
422   Deregisters the socket from the backend and cancels pending 422   Deregisters the socket from the backend and cancels pending
423   operations without closing the descriptor. The caller takes 423   operations without closing the descriptor. The caller takes
424   ownership of the returned handle. 424   ownership of the returned handle.
425   425  
426   A closed object reports `errc::bad_file_descriptor` and the 426   A closed object reports `errc::bad_file_descriptor` and the
427   returned handle is the invalid sentinel ( -1, or 427   returned handle is the invalid sentinel ( -1, or
428   `INVALID_SOCKET` on Windows ), never zero. 428   `INVALID_SOCKET` on Windows ), never zero.
429   429  
430   @note With the IOCP backend, a socket released while operations 430   @note With the IOCP backend, a socket released while operations
431   on it are in flight stays bound to this context's completion 431   on it are in flight stays bound to this context's completion
432   port for good. Windows refuses to unbind a handle with I/O 432   port for good. Windows refuses to unbind a handle with I/O
433   outstanding, so no context, this one included, can assign it 433   outstanding, so no context, this one included, can assign it
434   afterwards. Its cancelled operations still complete normally. 434   afterwards. Its cancelled operations still complete normally.
435   435  
436   @return The error code and the native handle. 436   @return The error code and the native handle.
437   437  
438   @post is_open() == false 438   @post is_open() == false
439   */ 439   */
440   [[nodiscard]] capy::results<native_handle_type> release() noexcept; 440   [[nodiscard]] capy::results<native_handle_type> release() noexcept;
441   441  
442   /** Disable sends or receives on the socket. 442   /** Disable sends or receives on the socket.
443   443  
444   Unix stream connections are full-duplex: each direction 444   Unix stream connections are full-duplex: each direction
445   (send and receive) operates independently. This function 445   (send and receive) operates independently. This function
446   allows you to close one or both directions without 446   allows you to close one or both directions without
447   destroying the socket. 447   destroying the socket.
448   448  
449   Failures such as a peer that already disconnected are 449   Failures such as a peer that already disconnected are
450   normal runtime conditions and are reported through the 450   normal runtime conditions and are reported through the
451   returned error code. A closed socket reports 451   returned error code. A closed socket reports
452   `errc::bad_file_descriptor`. 452   `errc::bad_file_descriptor`.
453   453  
454   @param what Determines which operations are no longer 454   @param what Determines which operations are no longer
455   allowed. 455   allowed.
456   456  
457   @return The error code, empty on success. 457   @return The error code, empty on success.
458   */ 458   */
459   [[nodiscard]] std::error_code shutdown(shutdown_type what) noexcept; 459   [[nodiscard]] std::error_code shutdown(shutdown_type what) noexcept;
460   460  
461   /** Set a socket option. 461   /** Set a socket option.
462   462  
463   Applies a type-safe socket option to the underlying socket. 463   Applies a type-safe socket option to the underlying socket.
464   The option type encodes the protocol level and option name. 464   The option type encodes the protocol level and option name.
465   465  
466   Fails with `errc::bad_file_descriptor` if the socket is not 466   Fails with `errc::bad_file_descriptor` if the socket is not
467   open, or with the error the operating system reports. 467   open, or with the error the operating system reports.
468   468  
469   @param opt The option to set. 469   @param opt The option to set.
470   470  
471   @return The error code, empty on success. 471   @return The error code, empty on success.
472   */ 472   */
473   template<class Option> 473   template<class Option>
HITCBC 474   14 [[nodiscard]] std::error_code set_option(Option const& opt) noexcept 474   14 [[nodiscard]] std::error_code set_option(Option const& opt) noexcept
475   { 475   {
HITCBC 476   14 if (!is_open()) 476   14 if (!is_open())
HITCBC 477   2 return make_error_code(std::errc::bad_file_descriptor); 477   2 return make_error_code(std::errc::bad_file_descriptor);
HITCBC 478   12 auto const fam = get().family(); 478   12 auto const fam = get().family();
HITCBC 479   12 return get().set_option( 479   12 return get().set_option(
HITCBC 480   12 opt.level(fam), opt.name(fam), opt.data(fam), opt.size(fam)); 480   12 opt.level(fam), opt.name(fam), opt.data(fam), opt.size(fam));
481   } 481   }
482   482  
483   /** Get a socket option. 483   /** Get a socket option.
484   484  
485   Retrieves the current value of a type-safe socket option. 485   Retrieves the current value of a type-safe socket option.
486   486  
487   Fails with `errc::bad_file_descriptor` if the socket is not 487   Fails with `errc::bad_file_descriptor` if the socket is not
488   open, or with the error the operating system reports. On 488   open, or with the error the operating system reports. On
489   failure the option is value-initialized. 489   failure the option is value-initialized.
490   490  
491   @return The error code and the option value. 491   @return The error code and the option value.
492   */ 492   */
493   template<class Option> 493   template<class Option>
HITCBC 494   10 [[nodiscard]] capy::results<Option> get_option() const noexcept 494   10 [[nodiscard]] capy::results<Option> get_option() const noexcept
495   { 495   {
HITCBC 496   10 if (!is_open()) 496   10 if (!is_open())
HITCBC 497   2 return {make_error_code(std::errc::bad_file_descriptor), Option{}}; 497   2 return {make_error_code(std::errc::bad_file_descriptor), Option{}};
HITCBC 498   8 Option opt{}; 498   8 Option opt{};
HITCBC 499   8 auto const fam = get().family(); 499   8 auto const fam = get().family();
HITCBC 500   8 std::size_t sz = opt.size(fam); 500   8 std::size_t sz = opt.size(fam);
HITCBC 501   8 if (auto ec = get().get_option( 501   8 if (auto ec = get().get_option(
502   opt.level(fam), opt.name(fam), opt.data(fam), &sz)) 502   opt.level(fam), opt.name(fam), opt.data(fam), &sz))
HITCBC 503   2 return {ec, Option{}}; 503   2 return {ec, Option{}};
HITCBC 504   6 opt.resize(fam, sz); 504   6 opt.resize(fam, sz);
HITCBC 505   6 return {std::error_code{}, std::move(opt)}; 505   6 return {std::error_code{}, std::move(opt)};
506   } 506   }
507   507  
508   /** Assign an existing native socket to this object. 508   /** Assign an existing native socket to this object.
509   509  
510   Adopts a Unix domain stream socket created outside the 510   Adopts a Unix domain stream socket created outside the
511   library — from `socketpair()`, received over `SCM_RIGHTS`, 511   library — from `socketpair()`, received over `SCM_RIGHTS`,
512   or made natively — and registers it with the backend. The 512   or made natively — and registers it with the backend. The
513   socket must be a stream socket in the `AF_UNIX` family. 513   socket must be a stream socket in the `AF_UNIX` family.
514   Adoption never alters the descriptor's flags or options: on 514   Adoption never alters the descriptor's flags or options: on
515   POSIX the fd must already be non-blocking, and on Windows 515   POSIX the fd must already be non-blocking, and on Windows
516   the socket must be overlapped-capable. 516   the socket must be overlapped-capable.
517   517  
518   The object must be closed. To replace a held socket, `close()` 518   The object must be closed. To replace a held socket, `close()`
519   or `release()` it first. 519   or `release()` it first.
520   520  
521   @par Exception Safety 521   @par Exception Safety
522   Throws nothing. On failure the object is unchanged and the 522   Throws nothing. On failure the object is unchanged and the
523   caller retains ownership of `fd`. 523   caller retains ownership of `fd`.
524   524  
525   @param fd The native socket to adopt. On success the object 525   @param fd The native socket to adopt. On success the object
526   owns it and closes it. 526   owns it and closes it.
527   527  
528   @return `error::already_open` if this object is open. 528   @return `error::already_open` if this object is open.
529   Otherwise the error code, empty on success. Validation and 529   Otherwise the error code, empty on success. Validation and
530   registration failures are normal runtime conditions when 530   registration failures are normal runtime conditions when
531   adopting foreign descriptors. 531   adopting foreign descriptors.
532   */ 532   */
533   [[nodiscard]] std::error_code assign(native_handle_type fd) noexcept; 533   [[nodiscard]] std::error_code assign(native_handle_type fd) noexcept;
534   534  
535   /** Get the local endpoint of the socket. 535   /** Get the local endpoint of the socket.
536   536  
537   Returns the local address (path) to which the socket is bound. 537   Returns the local address (path) to which the socket is bound.
538   The endpoint is cached when the connection is established. 538   The endpoint is cached when the connection is established.
539   539  
540   @return The local endpoint, or a default endpoint if the socket 540   @return The local endpoint, or a default endpoint if the socket
541   is not connected. 541   is not connected.
542   */ 542   */
543   corosio::local_endpoint local_endpoint() const noexcept; 543   corosio::local_endpoint local_endpoint() const noexcept;
544   544  
545   /** Get the remote endpoint of the socket. 545   /** Get the remote endpoint of the socket.
546   546  
547   Returns the remote address (path) to which the socket is connected. 547   Returns the remote address (path) to which the socket is connected.
548   The endpoint is cached when the connection is established. 548   The endpoint is cached when the connection is established.
549   549  
550   @return The remote endpoint, or a default endpoint if the socket 550   @return The remote endpoint, or a default endpoint if the socket
551   is not connected. 551   is not connected.
552   */ 552   */
553   corosio::local_endpoint remote_endpoint() const noexcept; 553   corosio::local_endpoint remote_endpoint() const noexcept;
554   554  
555   protected: 555   protected:
556   /// Default construct a closed socket for a derived class to open. 556   /// Default construct a closed socket for a derived class to open.
HITCBC 557   46 local_stream_socket() noexcept = default; 557   46 local_stream_socket() noexcept = default;
558   558  
559   /** Adopt an existing handle. 559   /** Adopt an existing handle.
560   560  
561   @param h The handle the socket takes ownership of. 561   @param h The handle the socket takes ownership of.
562   */ 562   */
563   explicit local_stream_socket(handle h) noexcept : io_object(std::move(h)) {} 563   explicit local_stream_socket(handle h) noexcept : io_object(std::move(h)) {}
564   564  
565   private: 565   private:
566   friend class local_stream_acceptor; 566   friend class local_stream_acceptor;
567   567  
568   [[nodiscard]] std::error_code 568   [[nodiscard]] std::error_code
569   open_for_family(int family, int type, int protocol) noexcept; 569   open_for_family(int family, int type, int protocol) noexcept;
570   570  
HITCBC 571   1282 inline implementation& get() const noexcept 571   1282 inline implementation& get() const noexcept
572   { 572   {
HITCBC 573   1282 return *static_cast<implementation*>(h_.get()); 573   1282 return *static_cast<implementation*>(h_.get());
574   } 574   }
575   }; 575   };
576   576  
577   } // namespace boost::corosio 577   } // namespace boost::corosio
578   578  
579   #endif // BOOST_COROSIO_LOCAL_STREAM_SOCKET_HPP 579   #endif // BOOST_COROSIO_LOCAL_STREAM_SOCKET_HPP