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