100.00% Lines (85/85) 100.00% Functions (19/19)
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_ACCEPTOR_HPP 10   #ifndef BOOST_COROSIO_LOCAL_STREAM_ACCEPTOR_HPP
11   #define BOOST_COROSIO_LOCAL_STREAM_ACCEPTOR_HPP 11   #define BOOST_COROSIO_LOCAL_STREAM_ACCEPTOR_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/except.hpp> 15   #include <boost/corosio/detail/except.hpp>
16   #include <boost/corosio/detail/op_base.hpp> 16   #include <boost/corosio/detail/op_base.hpp>
17   #include <boost/corosio/wait_type.hpp> 17   #include <boost/corosio/wait_type.hpp>
18   #include <boost/corosio/io/io_object.hpp> 18   #include <boost/corosio/io/io_object.hpp>
19   #include <boost/capy/io_result.hpp> 19   #include <boost/capy/io_result.hpp>
20   #include <boost/corosio/local_endpoint.hpp> 20   #include <boost/corosio/local_endpoint.hpp>
21   #include <boost/corosio/local_stream_socket.hpp> 21   #include <boost/corosio/local_stream_socket.hpp>
22   #include <boost/capy/ex/executor_ref.hpp> 22   #include <boost/capy/ex/executor_ref.hpp>
23   #include <boost/capy/ex/execution_context.hpp> 23   #include <boost/capy/ex/execution_context.hpp>
24   #include <boost/capy/ex/io_env.hpp> 24   #include <boost/capy/ex/io_env.hpp>
25   #include <boost/capy/concept/executor.hpp> 25   #include <boost/capy/concept/executor.hpp>
26   26  
27   #include <system_error> 27   #include <system_error>
28   28  
29   #include <cassert> 29   #include <cassert>
30   #include <concepts> 30   #include <concepts>
31   #include <coroutine> 31   #include <coroutine>
32   #include <cstddef> 32   #include <cstddef>
33   #include <stop_token> 33   #include <stop_token>
34   #include <type_traits> 34   #include <type_traits>
35   35  
36   namespace boost::corosio { 36   namespace boost::corosio {
37   37  
38   /** Options for @ref local_stream_acceptor::bind(). 38   /** Options for @ref local_stream_acceptor::bind().
39   39  
40   Controls filesystem cleanup behavior before binding 40   Controls filesystem cleanup behavior before binding
41   to a Unix domain socket path. 41   to a Unix domain socket path.
42   */ 42   */
43   enum class bind_option 43   enum class bind_option
44   { 44   {
45   none, 45   none,
46   /// Unlink the socket path before binding (ignored for abstract paths). 46   /// Unlink the socket path before binding (ignored for abstract paths).
47   unlink_existing 47   unlink_existing
48   }; 48   };
49   49  
50   /** An asynchronous Unix domain stream acceptor for coroutine I/O. 50   /** An asynchronous Unix domain stream acceptor for coroutine I/O.
51   51  
52   This class provides asynchronous Unix domain stream accept 52   This class provides asynchronous Unix domain stream accept
53   operations that return awaitable types. The acceptor binds 53   operations that return awaitable types. The acceptor binds
54   to a local endpoint (filesystem path or abstract name) and 54   to a local endpoint (filesystem path or abstract name) and
55   listens for incoming connections. 55   listens for incoming connections.
56   56  
57   The library does NOT automatically unlink the socket path 57   The library does NOT automatically unlink the socket path
58   on close. Callers are responsible for removing the socket 58   on close. Callers are responsible for removing the socket
59   file before bind (via @ref bind_option::unlink_existing) or 59   file before bind (via @ref bind_option::unlink_existing) or
60   after close. 60   after close.
61   61  
62   @par Thread Safety 62   @par Thread Safety
63   Distinct objects: Safe.@n 63   Distinct objects: Safe.@n
64   Shared objects: Unsafe. An acceptor must not have concurrent 64   Shared objects: Unsafe. An acceptor must not have concurrent
65   accept operations. 65   accept operations.
66   66  
67   @par Example 67   @par Example
68   @par !example bind_listen_accept 68   @par !example bind_listen_accept
69   */ 69   */
70   class BOOST_COROSIO_DECL local_stream_acceptor : public io_object 70   class BOOST_COROSIO_DECL local_stream_acceptor : public io_object
71   { 71   {
72   struct wait_awaitable : detail::void_op_base<wait_awaitable> 72   struct wait_awaitable : detail::void_op_base<wait_awaitable>
73   { 73   {
74   local_stream_acceptor& acc_; 74   local_stream_acceptor& acc_;
75   wait_type w_; 75   wait_type w_;
76   76  
HITCBC 77   8 wait_awaitable(local_stream_acceptor& acc, wait_type w) noexcept 77   8 wait_awaitable(local_stream_acceptor& acc, wait_type w) noexcept
HITCBC 78   16 : acc_(acc) 78   16 : acc_(acc)
HITCBC 79   8 , w_(w) 79   8 , w_(w)
80   { 80   {
HITCBC 81   8 } 81   8 }
82   82  
83   std::coroutine_handle<> 83   std::coroutine_handle<>
HITCBC 84   6 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const 84   6 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
85   { 85   {
HITCBC 86   6 return acc_.get().wait(h, ex, w_, token_, &ec_); 86   6 return acc_.get().wait(h, ex, w_, token_, &ec_);
87   } 87   }
88   }; 88   };
89   89  
90   struct move_accept_awaitable : detail::void_op_base<move_accept_awaitable> 90   struct move_accept_awaitable : detail::void_op_base<move_accept_awaitable>
91   { 91   {
92   local_stream_acceptor& acc_; 92   local_stream_acceptor& acc_;
93   mutable io_object::implementation* peer_impl_ = nullptr; 93   mutable io_object::implementation* peer_impl_ = nullptr;
94   94  
HITCBC 95   6 explicit move_accept_awaitable(local_stream_acceptor& acc) noexcept 95   6 explicit move_accept_awaitable(local_stream_acceptor& acc) noexcept
HITCBC 96   6 : acc_(acc) 96   6 : acc_(acc)
97   { 97   {
HITCBC 98   6 } 98   6 }
99   99  
100   [[nodiscard]] capy::io_result<local_stream_socket> 100   [[nodiscard]] capy::io_result<local_stream_socket>
HITCBC 101   6 await_resume() const noexcept 101   6 await_resume() const noexcept
102   { 102   {
HITCBC 103   6 if (this->ec_ || !peer_impl_) 103   6 if (this->ec_ || !peer_impl_)
HITCBC 104   4 return {this->ec_, local_stream_socket()}; 104   4 return {this->ec_, local_stream_socket()};
105   105  
HITCBC 106   2 local_stream_socket peer(acc_.ctx_); 106   2 local_stream_socket peer(acc_.ctx_);
HITCBC 107   2 reset_peer_impl(peer, peer_impl_); 107   2 reset_peer_impl(peer, peer_impl_);
HITCBC 108   2 return {this->ec_, std::move(peer)}; 108   2 return {this->ec_, std::move(peer)};
HITCBC 109   2 } 109   2 }
110   110  
111   std::coroutine_handle<> 111   std::coroutine_handle<>
HITCBC 112   4 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const 112   4 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
113   { 113   {
HITCBC 114   12 return acc_.get().accept( 114   12 return acc_.get().accept(
HITCBC 115   12 h, ex, this->token_, &this->ec_, &peer_impl_); 115   12 h, ex, this->token_, &this->ec_, &peer_impl_);
116   } 116   }
117   }; 117   };
118   118  
119   struct accept_awaitable : detail::void_op_base<accept_awaitable> 119   struct accept_awaitable : detail::void_op_base<accept_awaitable>
120   { 120   {
121   local_stream_acceptor& acc_; 121   local_stream_acceptor& acc_;
122   local_stream_socket& peer_; 122   local_stream_socket& peer_;
123   mutable io_object::implementation* peer_impl_ = nullptr; 123   mutable io_object::implementation* peer_impl_ = nullptr;
124   124  
HITCBC 125   29 accept_awaitable( 125   29 accept_awaitable(
126   local_stream_acceptor& acc, local_stream_socket& peer) noexcept 126   local_stream_acceptor& acc, local_stream_socket& peer) noexcept
HITCBC 127   58 : acc_(acc) 127   58 : acc_(acc)
HITCBC 128   29 , peer_(peer) 128   29 , peer_(peer)
129   { 129   {
HITCBC 130   29 } 130   29 }
131   131  
HITCBC 132   27 [[nodiscard]] capy::io_result<> await_resume() const noexcept 132   27 [[nodiscard]] capy::io_result<> await_resume() const noexcept
133   { 133   {
HITCBC 134   27 if (!this->ec_ && peer_impl_) 134   27 if (!this->ec_ && peer_impl_)
HITCBC 135   17 peer_.h_.reset(peer_impl_); 135   17 peer_.h_.reset(peer_impl_);
HITCBC 136   27 return {this->ec_}; 136   27 return {this->ec_};
137   } 137   }
138   138  
139   std::coroutine_handle<> 139   std::coroutine_handle<>
HITCBC 140   25 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const 140   25 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
141   { 141   {
HITCBC 142   75 return acc_.get().accept( 142   75 return acc_.get().accept(
HITCBC 143   75 h, ex, this->token_, &this->ec_, &peer_impl_); 143   75 h, ex, this->token_, &this->ec_, &peer_impl_);
144   } 144   }
145   }; 145   };
146   146  
147   public: 147   public:
148   /** Destructor. 148   /** Destructor.
149   149  
150   Closes the acceptor if open, cancelling any pending operations. 150   Closes the acceptor if open, cancelling any pending operations.
151   */ 151   */
152   ~local_stream_acceptor() override; 152   ~local_stream_acceptor() override;
153   153  
154   /** Construct an acceptor from an execution context. 154   /** Construct an acceptor from an execution context.
155   155  
156   @param ctx The execution context that will own this acceptor. 156   @param ctx The execution context that will own this acceptor.
157   */ 157   */
158   explicit local_stream_acceptor(capy::execution_context& ctx); 158   explicit local_stream_acceptor(capy::execution_context& ctx);
159   159  
160   /** Convenience constructor: open + bind + listen. 160   /** Convenience constructor: open + bind + listen.
161   161  
162   Creates a fully-bound listening acceptor in a single 162   Creates a fully-bound listening acceptor in a single
163   expression, throwing the codes the piecewise `open()` + 163   expression, throwing the codes the piecewise `open()` +
164   `bind()` + `listen()` path returns. 164   `bind()` + `listen()` path returns.
165   165  
166   @param ctx The execution context that will own this acceptor. 166   @param ctx The execution context that will own this acceptor.
167   @param ep The local endpoint to bind to. 167   @param ep The local endpoint to bind to.
168   @param backlog The maximum pending connection queue length. 168   @param backlog The maximum pending connection queue length.
169   169  
170   @throws std::system_error on open, bind, or listen failure. 170   @throws std::system_error on open, bind, or listen failure.
171   */ 171   */
172   local_stream_acceptor( 172   local_stream_acceptor(
173   capy::execution_context& ctx, 173   capy::execution_context& ctx,
174   corosio::local_endpoint ep, 174   corosio::local_endpoint ep,
175   int backlog = 128); 175   int backlog = 128);
176   176  
177   /** Construct an acceptor from an executor. 177   /** Construct an acceptor from an executor.
178   178  
179   The acceptor is associated with the executor's context. 179   The acceptor is associated with the executor's context.
180   180  
181   @param ex The executor whose context will own the acceptor. 181   @param ex The executor whose context will own the acceptor.
182   182  
183   @tparam Ex A type satisfying @ref capy::Executor. Must not 183   @tparam Ex A type satisfying @ref capy::Executor. Must not
184   be `local_stream_acceptor` itself (disables implicit 184   be `local_stream_acceptor` itself (disables implicit
185   conversion from move). 185   conversion from move).
186   */ 186   */
187   template<class Ex> 187   template<class Ex>
188   requires(!std:: 188   requires(!std::
189   same_as<std::remove_cvref_t<Ex>, local_stream_acceptor>) && 189   same_as<std::remove_cvref_t<Ex>, local_stream_acceptor>) &&
190   capy::Executor<Ex> 190   capy::Executor<Ex>
191   explicit local_stream_acceptor(Ex const& ex) 191   explicit local_stream_acceptor(Ex const& ex)
192   : local_stream_acceptor(ex.context()) 192   : local_stream_acceptor(ex.context())
193   { 193   {
194   } 194   }
195   195  
196   /** Convenience constructor from an executor. 196   /** Convenience constructor from an executor.
197   197  
198   @param ex The executor whose context will own the acceptor. 198   @param ex The executor whose context will own the acceptor.
199   @param ep The local endpoint to bind to. 199   @param ep The local endpoint to bind to.
200   @param backlog The maximum pending connection queue length. 200   @param backlog The maximum pending connection queue length.
201   201  
202   @throws std::system_error on open, bind, or listen failure. 202   @throws std::system_error on open, bind, or listen failure.
203   */ 203   */
204   template<class Ex> 204   template<class Ex>
205   requires capy::Executor<Ex> 205   requires capy::Executor<Ex>
206   local_stream_acceptor( 206   local_stream_acceptor(
207   Ex const& ex, corosio::local_endpoint ep, int backlog = 128) 207   Ex const& ex, corosio::local_endpoint ep, int backlog = 128)
208   : local_stream_acceptor(ex.context(), std::move(ep), backlog) 208   : local_stream_acceptor(ex.context(), std::move(ep), backlog)
209   { 209   {
210   } 210   }
211   211  
212   /** Move constructor. 212   /** Move constructor.
213   213  
214   Transfers ownership of the acceptor resources. 214   Transfers ownership of the acceptor resources.
215   215  
216   @param other The acceptor to move from. 216   @param other The acceptor to move from.
217   217  
218   @pre No awaitables returned by @p other's methods exist. 218   @pre No awaitables returned by @p other's methods exist.
219   @pre The execution context associated with @p other must 219   @pre The execution context associated with @p other must
220   outlive this acceptor. 220   outlive this acceptor.
221   */ 221   */
HITCBC 222   2 local_stream_acceptor(local_stream_acceptor&& other) noexcept 222   2 local_stream_acceptor(local_stream_acceptor&& other) noexcept
HITCBC 223   2 : local_stream_acceptor(other.ctx_, std::move(other)) 223   2 : local_stream_acceptor(other.ctx_, std::move(other))
224   { 224   {
HITCBC 225   2 } 225   2 }
226   226  
227   /** Move assignment operator. 227   /** Move assignment operator.
228   228  
229   Closes any existing acceptor and transfers ownership. 229   Closes any existing acceptor and transfers ownership.
230   Both acceptors must share the same execution context. 230   Both acceptors must share the same execution context.
231   231  
232   @param other The acceptor to move from. 232   @param other The acceptor to move from.
233   233  
234   @return Reference to this acceptor. 234   @return Reference to this acceptor.
235   235  
236   @pre `&ctx_ == &other.ctx_` (same execution context). 236   @pre `&ctx_ == &other.ctx_` (same execution context).
237   @pre No awaitables returned by either `*this` or @p other's 237   @pre No awaitables returned by either `*this` or @p other's
238   methods exist. 238   methods exist.
239   */ 239   */
240   local_stream_acceptor& operator=(local_stream_acceptor&& other) noexcept 240   local_stream_acceptor& operator=(local_stream_acceptor&& other) noexcept
241   { 241   {
242   assert( 242   assert(
243   &ctx_ == &other.ctx_ && 243   &ctx_ == &other.ctx_ &&
244   "move-assign requires the same execution_context"); 244   "move-assign requires the same execution_context");
245   if (this != &other) 245   if (this != &other)
246   { 246   {
247   close(); 247   close();
248   io_object::operator=(std::move(other)); 248   io_object::operator=(std::move(other));
249   } 249   }
250   return *this; 250   return *this;
251   } 251   }
252   252  
253   local_stream_acceptor(local_stream_acceptor const&) = delete; 253   local_stream_acceptor(local_stream_acceptor const&) = delete;
254   local_stream_acceptor& operator=(local_stream_acceptor const&) = delete; 254   local_stream_acceptor& operator=(local_stream_acceptor const&) = delete;
255   255  
256   /** Create the acceptor socket. 256   /** Create the acceptor socket.
257   257  
258   Failures such as descriptor exhaustion are normal runtime 258   Failures such as descriptor exhaustion are normal runtime
259   conditions and are reported through the returned error code. 259   conditions and are reported through the returned error code.
260   260  
261   261  
262   @return The error code, empty on success. 262   @return The error code, empty on success.
263   */ 263   */
264   [[nodiscard]] std::error_code open() noexcept; 264   [[nodiscard]] std::error_code open() noexcept;
265   265  
266   /** Bind to a local endpoint. 266   /** Bind to a local endpoint.
267   267  
268   @param ep The local endpoint (path) to bind to. 268   @param ep The local endpoint (path) to bind to.
269   @param opt Bind options. Pass bind_option::unlink_existing 269   @param opt Bind options. Pass bind_option::unlink_existing
270   to unlink the socket path before binding (ignored for 270   to unlink the socket path before binding (ignored for
271   abstract sockets and empty endpoints). 271   abstract sockets and empty endpoints).
272   272  
273   @return An error code on failure, empty on success. 273   @return An error code on failure, empty on success.
274   274  
275   A closed acceptor reports `errc::bad_file_descriptor`. 275   A closed acceptor reports `errc::bad_file_descriptor`.
276   */ 276   */
277   [[nodiscard]] std::error_code bind( 277   [[nodiscard]] std::error_code bind(
278   corosio::local_endpoint ep, 278   corosio::local_endpoint ep,
279   bind_option opt = bind_option::none) noexcept; 279   bind_option opt = bind_option::none) noexcept;
280   280  
281   /** Start listening for incoming connections. 281   /** Start listening for incoming connections.
282   282  
283   @param backlog The maximum pending connection queue length. 283   @param backlog The maximum pending connection queue length.
284   284  
285   @return An error code on failure, empty on success. 285   @return An error code on failure, empty on success.
286   286  
287   A closed acceptor reports `errc::bad_file_descriptor`. 287   A closed acceptor reports `errc::bad_file_descriptor`.
288   */ 288   */
289   [[nodiscard]] std::error_code listen(int backlog = 128) noexcept; 289   [[nodiscard]] std::error_code listen(int backlog = 128) noexcept;
290   290  
291   /** Close the acceptor. 291   /** Close the acceptor.
292   292  
293   Cancels any pending accept operations and releases the 293   Cancels any pending accept operations and releases the
294   underlying socket. Has no effect if the acceptor is not 294   underlying socket. Has no effect if the acceptor is not
295   open. 295   open.
296   296  
297   @post is_open() == false 297   @post is_open() == false
298   */ 298   */
299   void close() noexcept; 299   void close() noexcept;
300   300  
301   /// Check if the acceptor has an open socket handle. 301   /// Check if the acceptor has an open socket handle.
HITCBC 302   489 bool is_open() const noexcept 302   489 bool is_open() const noexcept
303   { 303   {
HITCBC 304   489 return h_ && get().is_open(); 304   489 return h_ && get().is_open();
305   } 305   }
306   306  
307   /** Initiate an asynchronous accept into an existing socket. 307   /** Initiate an asynchronous accept into an existing socket.
308   308  
309   Completes when a new connection is available. On success 309   Completes when a new connection is available. On success
310   @p peer is reset to the accepted connection. Only one 310   @p peer is reset to the accepted connection. Only one
311   accept may be in flight at a time. 311   accept may be in flight at a time.
312   312  
313   @param peer The socket to receive the accepted connection. 313   @param peer The socket to receive the accepted connection.
314   314  
315   @par Cancellation 315   @par Cancellation
316   Supports cancellation via stop_token or cancel(). 316   Supports cancellation via stop_token or cancel().
317   On cancellation, yields `capy::cond::canceled` and 317   On cancellation, yields `capy::cond::canceled` and
318   @p peer is not modified. 318   @p peer is not modified.
319   319  
320   @return An awaitable that completes with io_result<>. 320   @return An awaitable that completes with io_result<>.
321   321  
322   A closed acceptor reports `errc::bad_file_descriptor`. 322   A closed acceptor reports `errc::bad_file_descriptor`.
323   */ 323   */
HITCBC 324   29 [[nodiscard]] auto accept(local_stream_socket& peer) 324   29 [[nodiscard]] auto accept(local_stream_socket& peer)
325   { 325   {
HITCBC 326   29 accept_awaitable aw(*this, peer); 326   29 accept_awaitable aw(*this, peer);
HITCBC 327   29 if (!is_open()) 327   29 if (!is_open())
HITCBC 328   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 328   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 329   29 return aw; 329   29 return aw;
330   } 330   }
331   331  
332   /** Wait for an incoming connection or readiness condition. 332   /** Wait for an incoming connection or readiness condition.
333   333  
334   Suspends until the listen socket is ready in the 334   Suspends until the listen socket is ready in the
335   requested direction. For `wait_type::read`, completion 335   requested direction. For `wait_type::read`, completion
336   signals that a subsequent @ref accept will succeed 336   signals that a subsequent @ref accept will succeed
337   without blocking; a connection already queued when the 337   without blocking; a connection already queued when the
338   wait begins completes it immediately. No connection is 338   wait begins completes it immediately. No connection is
339   consumed. 339   consumed.
340   340  
341   @note `wait_type::write` is not usable on an acceptor: 341   @note `wait_type::write` is not usable on an acceptor:
342   writability carries no meaning for a listening socket, so 342   writability carries no meaning for a listening socket, so
343   the wait fails with `errc::operation_not_supported` on 343   the wait fails with `errc::operation_not_supported` on
344   every backend. 344   every backend.
345   345  
346   @param w The wait direction. 346   @param w The wait direction.
347   347  
348   @return An awaitable that completes with `io_result<>`. 348   @return An awaitable that completes with `io_result<>`.
349   349  
350   A closed acceptor completes with `errc::bad_file_descriptor`. 350   A closed acceptor completes with `errc::bad_file_descriptor`.
351   351  
352   @par Preconditions 352   @par Preconditions
353   This acceptor must outlive the returned awaitable. 353   This acceptor must outlive the returned awaitable.
354   */ 354   */
HITCBC 355   8 [[nodiscard]] auto wait(wait_type w) 355   8 [[nodiscard]] auto wait(wait_type w)
356   { 356   {
HITCBC 357   8 wait_awaitable aw(*this, w); 357   8 wait_awaitable aw(*this, w);
HITCBC 358   8 if (!is_open()) 358   8 if (!is_open())
HITCBC 359   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 359   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 360   8 return aw; 360   8 return aw;
361   } 361   }
362   362  
363   /** Initiate an asynchronous accept, returning the socket. 363   /** Initiate an asynchronous accept, returning the socket.
364   364  
365   Completes when a new connection is available. Only one 365   Completes when a new connection is available. Only one
366   accept may be in flight at a time. 366   accept may be in flight at a time.
367   367  
368   @par Cancellation 368   @par Cancellation
369   Supports cancellation via stop_token or cancel(). 369   Supports cancellation via stop_token or cancel().
370   On cancellation, yields `capy::cond::canceled` with 370   On cancellation, yields `capy::cond::canceled` with
371   a default-constructed socket. 371   a default-constructed socket.
372   372  
373   @return An awaitable that completes with 373   @return An awaitable that completes with
374   io_result<local_stream_socket>. 374   io_result<local_stream_socket>.
375   375  
376   A closed acceptor reports `errc::bad_file_descriptor`. 376   A closed acceptor reports `errc::bad_file_descriptor`.
377   On failure the returned socket is default-constructed and 377   On failure the returned socket is default-constructed and
378   may only be destroyed or assigned. 378   may only be destroyed or assigned.
379   */ 379   */
HITCBC 380   6 [[nodiscard]] auto accept() 380   6 [[nodiscard]] auto accept()
381   { 381   {
HITCBC 382   6 move_accept_awaitable aw(*this); 382   6 move_accept_awaitable aw(*this);
HITCBC 383   6 if (!is_open()) 383   6 if (!is_open())
HITCBC 384   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 384   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 385   6 return aw; 385   6 return aw;
386   } 386   }
387   387  
388   /** Cancel pending asynchronous accept operations. 388   /** Cancel pending asynchronous accept operations.
389   389  
390   Outstanding accept operations complete with 390   Outstanding accept operations complete with
391   @c capy::cond::canceled. Safe to call when no 391   @c capy::cond::canceled. Safe to call when no
392   operations are pending (no-op). 392   operations are pending (no-op).
393   */ 393   */
394   void cancel() noexcept; 394   void cancel() noexcept;
395   395  
396   /** Release ownership of the native socket handle. 396   /** Release ownership of the native socket handle.
397   397  
398   Deregisters the acceptor from the reactor and cancels 398   Deregisters the acceptor from the reactor and cancels
399   pending operations without closing the descriptor. The 399   pending operations without closing the descriptor. The
400   caller takes ownership of the returned handle. 400   caller takes ownership of the returned handle.
401   401  
402   @return The native handle. 402   @return The native handle.
403   403  
404   @throws std::system_error `errc::bad_file_descriptor` if the 404   @throws std::system_error `errc::bad_file_descriptor` if the
405   acceptor is not open. 405   acceptor is not open.
406   406  
407   @post is_open() == false 407   @post is_open() == false
408   */ 408   */
409   native_handle_type release(); 409   native_handle_type release();
410   410  
411   /** Get the native socket handle. 411   /** Get the native socket handle.
412   412  
413   @return The native socket handle, or -1/INVALID_SOCKET if not 413   @return The native socket handle, or -1/INVALID_SOCKET if not
414   open. 414   open.
415   415  
416   @par Preconditions 416   @par Preconditions
417   None. May be called on closed acceptors. 417   None. May be called on closed acceptors.
418   */ 418   */
419   native_handle_type native_handle() const noexcept; 419   native_handle_type native_handle() const noexcept;
420   420  
421   /** Assign an existing native socket to this acceptor. 421   /** Assign an existing native socket to this acceptor.
422   422  
423   Adopts a listening socket created outside the library — 423   Adopts a listening socket created outside the library —
424   received from a service manager, inherited, or made natively — 424   received from a service manager, inherited, or made natively —
425   and registers it with the backend. The socket must be a 425   and registers it with the backend. The socket must be a
426   listening stream socket in the local IPC family. Adoption 426   listening stream socket in the local IPC family. Adoption
427   never alters the descriptor's flags or options: on POSIX the 427   never alters the descriptor's flags or options: on POSIX the
428   fd must already be non-blocking, and on Windows the socket 428   fd must already be non-blocking, and on Windows the socket
429   must be overlapped-capable. 429   must be overlapped-capable.
430   430  
431   Adoption does not verify listen state; @ref accept reports the 431   Adoption does not verify listen state; @ref accept reports the
432   error if the socket is not listening. 432   error if the socket is not listening.
433   433  
434   If this object is already open, pending operations complete 434   If this object is already open, pending operations complete
435   with `errc::operation_canceled` and the held socket is closed 435   with `errc::operation_canceled` and the held socket is closed
436   before the new one is adopted. 436   before the new one is adopted.
437   437  
438   @par Exception Safety 438   @par Exception Safety
439   Strong guarantee on validation failure: the object is 439   Strong guarantee on validation failure: the object is
440   unchanged. If backend registration fails, the object either 440   unchanged. If backend registration fails, the object either
441   retains its previous socket or is left closed, depending on 441   retains its previous socket or is left closed, depending on
442   the backend. In all failure cases the caller retains 442   the backend. In all failure cases the caller retains
443   ownership of `fd`. 443   ownership of `fd`.
444   444  
445   @param fd The native socket to adopt. On success the object 445   @param fd The native socket to adopt. On success the object
446   owns it and will close it. 446   owns it and will close it.
447   447  
448   @return The error code, empty on success. Validation and 448   @return The error code, empty on success. Validation and
449   registration failures are normal runtime conditions when 449   registration failures are normal runtime conditions when
450   adopting foreign descriptors. 450   adopting foreign descriptors.
451   */ 451   */
452   [[nodiscard]] std::error_code assign(native_handle_type fd) noexcept; 452   [[nodiscard]] std::error_code assign(native_handle_type fd) noexcept;
453   453  
454   /** Return the local endpoint the acceptor is bound to. 454   /** Return the local endpoint the acceptor is bound to.
455   455  
456   Returns a default-constructed (empty) endpoint if the 456   Returns a default-constructed (empty) endpoint if the
457   acceptor is not open or not yet bound. Safe to call in 457   acceptor is not open or not yet bound. Safe to call in
458   any state. 458   any state.
459   */ 459   */
460   corosio::local_endpoint local_endpoint() const noexcept; 460   corosio::local_endpoint local_endpoint() const noexcept;
461   461  
462   /** Set a socket option on the acceptor. 462   /** Set a socket option on the acceptor.
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   @param opt The option to set. 467   @param opt The option to set.
468   468  
469   @tparam Option A socket option type providing static 469   @tparam Option A socket option type providing static
470   `level()` and `name()` members, and `data()` / `size()` 470   `level()` and `name()` members, and `data()` / `size()`
471   accessors. 471   accessors.
472   472  
473   @throws std::system_error `errc::bad_file_descriptor` if the 473   @throws std::system_error `errc::bad_file_descriptor` if the
474   acceptor is not open; otherwise thrown on failure. 474   acceptor is not open; otherwise thrown on failure.
475   */ 475   */
476   template<class Option> 476   template<class Option>
HITCBC 477   6 void set_option(Option const& opt) 477   6 void set_option(Option const& opt)
478   { 478   {
HITCBC 479   6 if (!is_open()) 479   6 if (!is_open())
HITCBC 480   2 detail::throw_system_error( 480   2 detail::throw_system_error(
HITCBC 481   4 make_error_code(std::errc::bad_file_descriptor), 481   4 make_error_code(std::errc::bad_file_descriptor),
482   "local_stream_acceptor::set_option"); 482   "local_stream_acceptor::set_option");
HITCBC 483   4 auto const fam = get().family(); 483   4 auto const fam = get().family();
HITCBC 484   4 std::error_code ec = get().set_option( 484   4 std::error_code ec = get().set_option(
485   opt.level(fam), opt.name(fam), opt.data(fam), opt.size(fam)); 485   opt.level(fam), opt.name(fam), opt.data(fam), opt.size(fam));
HITCBC 486   4 if (ec) 486   4 if (ec)
HITCBC 487   2 detail::throw_system_error(ec, "local_stream_acceptor::set_option"); 487   2 detail::throw_system_error(ec, "local_stream_acceptor::set_option");
HITCBC 488   2 } 488   2 }
489   489  
490   /** Get a socket option from the acceptor. 490   /** Get a socket option from the acceptor.
491   491  
492   Retrieves the current value of a type-safe socket option. 492   Retrieves the current value of a type-safe socket option.
493   493  
494   @return The current option value. 494   @return The current option value.
495   495  
496   @tparam Option A socket option type providing static 496   @tparam Option A socket option type providing static
497   `level()` and `name()` members, and `data()` / `size()` 497   `level()` and `name()` members, and `data()` / `size()`
498   / `resize()` members. 498   / `resize()` members.
499   499  
500   @throws std::system_error `errc::bad_file_descriptor` if the 500   @throws std::system_error `errc::bad_file_descriptor` if the
501   acceptor is not open; otherwise thrown on failure. 501   acceptor is not open; otherwise thrown on failure.
502   */ 502   */
503   template<class Option> 503   template<class Option>
HITCBC 504   6 Option get_option() const 504   6 Option get_option() const
505   { 505   {
HITCBC 506   6 if (!is_open()) 506   6 if (!is_open())
HITCBC 507   2 detail::throw_system_error( 507   2 detail::throw_system_error(
HITCBC 508   4 make_error_code(std::errc::bad_file_descriptor), 508   4 make_error_code(std::errc::bad_file_descriptor),
509   "local_stream_acceptor::get_option"); 509   "local_stream_acceptor::get_option");
HITCBC 510   4 Option opt{}; 510   4 Option opt{};
HITCBC 511   4 auto const fam = get().family(); 511   4 auto const fam = get().family();
HITCBC 512   4 std::size_t sz = opt.size(fam); 512   4 std::size_t sz = opt.size(fam);
513   std::error_code ec = 513   std::error_code ec =
HITCBC 514   4 get().get_option(opt.level(fam), opt.name(fam), opt.data(fam), &sz); 514   4 get().get_option(opt.level(fam), opt.name(fam), opt.data(fam), &sz);
HITCBC 515   4 if (ec) 515   4 if (ec)
HITCBC 516   2 detail::throw_system_error(ec, "local_stream_acceptor::get_option"); 516   2 detail::throw_system_error(ec, "local_stream_acceptor::get_option");
HITCBC 517   2 opt.resize(fam, sz); 517   2 opt.resize(fam, sz);
HITCBC 518   2 return opt; 518   2 return opt;
519   } 519   }
520   520  
521   /** Backend hooks for local stream acceptor operations. 521   /** Backend hooks for local stream acceptor operations.
522   522  
523   Platform backends derive from this to implement 523   Platform backends derive from this to implement
524   accept, option, and lifecycle management. 524   accept, option, and lifecycle management.
525   */ 525   */
526   struct implementation : io_object::implementation 526   struct implementation : io_object::implementation
527   { 527   {
528   /** Initiate an asynchronous accept. 528   /** Initiate an asynchronous accept.
529   529  
530   On completion the backend sets @p *ec and, on 530   On completion the backend sets @p *ec and, on
531   success, stores a pointer to the new socket 531   success, stores a pointer to the new socket
532   implementation in @p *impl_out. 532   implementation in @p *impl_out.
533   533  
534   @param h Coroutine handle to resume. 534   @param h Coroutine handle to resume.
535   @param ex Executor for dispatching the completion. 535   @param ex Executor for dispatching the completion.
536   @param token Stop token for cancellation. 536   @param token Stop token for cancellation.
537   @param ec Output error code. 537   @param ec Output error code.
538   @param impl_out Output pointer for the accepted socket. 538   @param impl_out Output pointer for the accepted socket.
539   @return Coroutine handle to resume immediately. 539   @return Coroutine handle to resume immediately.
540   */ 540   */
541   virtual std::coroutine_handle<> accept( 541   virtual std::coroutine_handle<> accept(
542   std::coroutine_handle<>, 542   std::coroutine_handle<>,
543   capy::executor_ref, 543   capy::executor_ref,
544   std::stop_token, 544   std::stop_token,
545   std::error_code*, 545   std::error_code*,
546   io_object::implementation**) = 0; 546   io_object::implementation**) = 0;
547   547  
548   /** Initiate an asynchronous wait for acceptor readiness. 548   /** Initiate an asynchronous wait for acceptor readiness.
549   549  
550   Completes when the listen socket becomes ready for 550   Completes when the listen socket becomes ready for
551   the specified direction. No connection is consumed. 551   the specified direction. No connection is consumed.
552   */ 552   */
553   virtual std::coroutine_handle<> wait( 553   virtual std::coroutine_handle<> wait(
554   std::coroutine_handle<> h, 554   std::coroutine_handle<> h,
555   capy::executor_ref ex, 555   capy::executor_ref ex,
556   wait_type w, 556   wait_type w,
557   std::stop_token token, 557   std::stop_token token,
558   std::error_code* ec) = 0; 558   std::error_code* ec) = 0;
559   559  
560   /// Return the cached local endpoint. 560   /// Return the cached local endpoint.
561   virtual corosio::local_endpoint local_endpoint() const noexcept = 0; 561   virtual corosio::local_endpoint local_endpoint() const noexcept = 0;
562   562  
563   /// Return whether the underlying socket is open. 563   /// Return whether the underlying socket is open.
564   virtual bool is_open() const noexcept = 0; 564   virtual bool is_open() const noexcept = 0;
565   565  
566   /// Return the native handle, or the platform sentinel if closed. 566   /// Return the native handle, or the platform sentinel if closed.
567   virtual native_handle_type native_handle() const noexcept = 0; 567   virtual native_handle_type native_handle() const noexcept = 0;
568   568  
569   /** Return the socket's address family. 569   /** Return the socket's address family.
570   570  
571   Local sockets have no IP family; implementations return 571   Local sockets have no IP family; implementations return
572   `v4`, which the family-neutral options applicable to them 572   `v4`, which the family-neutral options applicable to them
573   ignore. 573   ignore.
574   574  
575   @return The address family for option rendering. 575   @return The address family for option rendering.
576   */ 576   */
577   virtual corosio::family family() const noexcept = 0; 577   virtual corosio::family family() const noexcept = 0;
578   578  
579   /// Release and return the native handle without closing. 579   /// Release and return the native handle without closing.
580   virtual native_handle_type release_socket() noexcept = 0; 580   virtual native_handle_type release_socket() noexcept = 0;
581   581  
582   /// Cancel pending accept operations. 582   /// Cancel pending accept operations.
583   virtual void cancel() noexcept = 0; 583   virtual void cancel() noexcept = 0;
584   584  
585   /// Set a raw socket option. 585   /// Set a raw socket option.
586   virtual std::error_code set_option( 586   virtual std::error_code set_option(
587   int level, 587   int level,
588   int optname, 588   int optname,
589   void const* data, 589   void const* data,
590   std::size_t size) noexcept = 0; 590   std::size_t size) noexcept = 0;
591   591  
592   /// Get a raw socket option. 592   /// Get a raw socket option.
593   virtual std::error_code 593   virtual std::error_code
594   get_option(int level, int optname, void* data, std::size_t* size) 594   get_option(int level, int optname, void* data, std::size_t* size)
595   const noexcept = 0; 595   const noexcept = 0;
596   }; 596   };
597   597  
598   protected: 598   protected:
HITCBC 599   18 local_stream_acceptor(handle h, capy::execution_context& ctx) noexcept 599   18 local_stream_acceptor(handle h, capy::execution_context& ctx) noexcept
HITCBC 600   18 : io_object(std::move(h)) 600   18 : io_object(std::move(h))
HITCBC 601   18 , ctx_(ctx) 601   18 , ctx_(ctx)
602   { 602   {
HITCBC 603   18 } 603   18 }
604   604  
HITCBC 605   2 local_stream_acceptor( 605   2 local_stream_acceptor(
606   capy::execution_context& ctx, local_stream_acceptor&& other) noexcept 606   capy::execution_context& ctx, local_stream_acceptor&& other) noexcept
HITCBC 607   2 : io_object(std::move(other)) 607   2 : io_object(std::move(other))
HITCBC 608   2 , ctx_(ctx) 608   2 , ctx_(ctx)
609   { 609   {
HITCBC 610   2 } 610   2 }
611   611  
HITCBC 612   8 static void reset_peer_impl( 612   8 static void reset_peer_impl(
613   local_stream_socket& peer, io_object::implementation* impl) noexcept 613   local_stream_socket& peer, io_object::implementation* impl) noexcept
614   { 614   {
HITCBC 615   8 if (impl) 615   8 if (impl)
HITCBC 616   8 peer.h_.reset(impl); 616   8 peer.h_.reset(impl);
HITCBC 617   8 } 617   8 }
618   618  
619   private: 619   private:
620   capy::execution_context& ctx_; 620   capy::execution_context& ctx_;
621   621  
HITCBC 622   572 inline implementation& get() const noexcept 622   572 inline implementation& get() const noexcept
623   { 623   {
HITCBC 624   572 return *static_cast<implementation*>(h_.get()); 624   572 return *static_cast<implementation*>(h_.get());
625   } 625   }
626   }; 626   };
627   627  
628   } // namespace boost::corosio 628   } // namespace boost::corosio
629   629  
630   #endif // BOOST_COROSIO_LOCAL_STREAM_ACCEPTOR_HPP 630   #endif // BOOST_COROSIO_LOCAL_STREAM_ACCEPTOR_HPP