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