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