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