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