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