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