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