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