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