88.24% Lines (45/51) 100.00% Functions (13/13)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2026 Michael Vandeberg 2   // Copyright (c) 2026 Michael Vandeberg
3   // 3   //
4   // Distributed under the Boost Software License, Version 1.0. (See accompanying 4   // Distributed under the Boost Software License, Version 1.0. (See accompanying
5   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6   // 6   //
7   // Official repository: https://github.com/cppalliance/corosio 7   // Official repository: https://github.com/cppalliance/corosio
8   // 8   //
9   9  
10   #ifndef BOOST_COROSIO_LOCAL_STREAM_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>
  16 + #include <boost/corosio/wait_type.hpp>
15   #include <boost/corosio/io/io_object.hpp> 17   #include <boost/corosio/io/io_object.hpp>
16   #include <boost/capy/io_result.hpp> 18   #include <boost/capy/io_result.hpp>
17   #include <boost/corosio/local_endpoint.hpp> 19   #include <boost/corosio/local_endpoint.hpp>
18   #include <boost/corosio/local_stream.hpp> 20   #include <boost/corosio/local_stream.hpp>
19   #include <boost/corosio/local_stream_socket.hpp> 21   #include <boost/corosio/local_stream_socket.hpp>
20   #include <boost/capy/ex/executor_ref.hpp> 22   #include <boost/capy/ex/executor_ref.hpp>
21   #include <boost/capy/ex/execution_context.hpp> 23   #include <boost/capy/ex/execution_context.hpp>
22   #include <boost/capy/ex/io_env.hpp> 24   #include <boost/capy/ex/io_env.hpp>
23   #include <boost/capy/concept/executor.hpp> 25   #include <boost/capy/concept/executor.hpp>
24   26  
25   #include <system_error> 27   #include <system_error>
26   28  
27   #include <cassert> 29   #include <cassert>
28   #include <concepts> 30   #include <concepts>
29   #include <coroutine> 31   #include <coroutine>
30   #include <cstddef> 32   #include <cstddef>
31   #include <stop_token> 33   #include <stop_token>
32   #include <type_traits> 34   #include <type_traits>
33   35  
34   namespace boost::corosio { 36   namespace boost::corosio {
35   37  
36   /** Options for @ref local_stream_acceptor::bind(). 38   /** Options for @ref local_stream_acceptor::bind().
37   39  
38   Controls filesystem cleanup behavior before binding 40   Controls filesystem cleanup behavior before binding
39   to a Unix domain socket path. 41   to a Unix domain socket path.
40   */ 42   */
41   enum class bind_option 43   enum class bind_option
42   { 44   {
43   none, 45   none,
44   /// Unlink the socket path before binding (ignored for abstract paths). 46   /// Unlink the socket path before binding (ignored for abstract paths).
45   unlink_existing 47   unlink_existing
46   }; 48   };
47   49  
48   /** An asynchronous Unix domain stream acceptor for coroutine I/O. 50   /** An asynchronous Unix domain stream acceptor for coroutine I/O.
49   51  
50   This class provides asynchronous Unix domain stream accept 52   This class provides asynchronous Unix domain stream accept
51   operations that return awaitable types. The acceptor binds 53   operations that return awaitable types. The acceptor binds
52   to a local endpoint (filesystem path or abstract name) and 54   to a local endpoint (filesystem path or abstract name) and
53   listens for incoming connections. 55   listens for incoming connections.
54   56  
55   The library does NOT automatically unlink the socket path 57   The library does NOT automatically unlink the socket path
56   on close. Callers are responsible for removing the socket 58   on close. Callers are responsible for removing the socket
57   file before bind (via @ref bind_option::unlink_existing) or 59   file before bind (via @ref bind_option::unlink_existing) or
58   after close. 60   after close.
59   61  
60   @par Thread Safety 62   @par Thread Safety
61   Distinct objects: Safe.@n 63   Distinct objects: Safe.@n
62   Shared objects: Unsafe. An acceptor must not have concurrent 64   Shared objects: Unsafe. An acceptor must not have concurrent
63   accept operations. 65   accept operations.
64   66  
65   @par Example 67   @par Example
66   @code 68   @code
67   io_context ioc; 69   io_context ioc;
68   local_stream_acceptor acc(ioc); 70   local_stream_acceptor acc(ioc);
69   acc.open(); 71   acc.open();
70   acc.bind(local_endpoint("/tmp/my.sock"), 72   acc.bind(local_endpoint("/tmp/my.sock"),
71   bind_option::unlink_existing); 73   bind_option::unlink_existing);
72   acc.listen(); 74   acc.listen();
73   auto [ec, peer] = co_await acc.accept(); 75   auto [ec, peer] = co_await acc.accept();
74   @endcode 76   @endcode
75   */ 77   */
76   class BOOST_COROSIO_DECL local_stream_acceptor : public io_object 78   class BOOST_COROSIO_DECL local_stream_acceptor : public io_object
77   { 79   {
  80 + struct wait_awaitable
  81 + : detail::void_op_base<wait_awaitable>
  82 + {
  83 + local_stream_acceptor& acc_;
  84 + wait_type w_;
  85 +
  86 + wait_awaitable(local_stream_acceptor& acc, wait_type w) noexcept
  87 + : acc_(acc), w_(w) {}
  88 +
  89 + std::coroutine_handle<> dispatch(
  90 + std::coroutine_handle<> h, capy::executor_ref ex) const
  91 + {
  92 + return acc_.get().wait(h, ex, w_, token_, &ec_);
  93 + }
  94 + };
  95 +
78   struct move_accept_awaitable 96   struct move_accept_awaitable
79   { 97   {
80   local_stream_acceptor& acc_; 98   local_stream_acceptor& acc_;
81   std::stop_token token_; 99   std::stop_token token_;
82   mutable std::error_code ec_; 100   mutable std::error_code ec_;
83   mutable io_object::implementation* peer_impl_ = nullptr; 101   mutable io_object::implementation* peer_impl_ = nullptr;
84   102  
HITCBC 85   2 explicit move_accept_awaitable( 103   2 explicit move_accept_awaitable(
86   local_stream_acceptor& acc) noexcept 104   local_stream_acceptor& acc) noexcept
HITCBC 87   2 : acc_(acc) 105   2 : acc_(acc)
88   { 106   {
HITCBC 89   2 } 107   2 }
90   108  
HITCBC 91   2 bool await_ready() const noexcept 109   2 bool await_ready() const noexcept
92   { 110   {
HITCBC 93   2 return token_.stop_requested(); 111   2 return token_.stop_requested();
94   } 112   }
95   113  
HITCBC 96   2 capy::io_result<local_stream_socket> await_resume() const noexcept 114   2 capy::io_result<local_stream_socket> await_resume() const noexcept
97   { 115   {
HITCBC 98   2 if (token_.stop_requested()) 116   2 if (token_.stop_requested())
MISUBC 99   return {make_error_code(std::errc::operation_canceled), 117   return {make_error_code(std::errc::operation_canceled),
MISUBC 100   local_stream_socket()}; 118   local_stream_socket()};
101   119  
HITCBC 102   2 if (ec_ || !peer_impl_) 120   2 if (ec_ || !peer_impl_)
MISUBC 103   return {ec_, local_stream_socket()}; 121   return {ec_, local_stream_socket()};
104   122  
HITCBC 105   2 local_stream_socket peer(acc_.ctx_); 123   2 local_stream_socket peer(acc_.ctx_);
HITCBC 106   2 reset_peer_impl(peer, peer_impl_); 124   2 reset_peer_impl(peer, peer_impl_);
HITCBC 107   2 return {ec_, std::move(peer)}; 125   2 return {ec_, std::move(peer)};
HITCBC 108   2 } 126   2 }
109   127  
HITCBC 110   2 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 128   2 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
111   -> std::coroutine_handle<> 129   -> std::coroutine_handle<>
112   { 130   {
HITCBC 113   2 token_ = env->stop_token; 131   2 token_ = env->stop_token;
HITCBC 114   6 return acc_.get().accept( 132   6 return acc_.get().accept(
HITCBC 115   6 h, env->executor, token_, &ec_, &peer_impl_); 133   6 h, env->executor, token_, &ec_, &peer_impl_);
116   } 134   }
117   }; 135   };
118   136  
119   struct accept_awaitable 137   struct accept_awaitable
120   { 138   {
121   local_stream_acceptor& acc_; 139   local_stream_acceptor& acc_;
122   local_stream_socket& peer_; 140   local_stream_socket& peer_;
123   std::stop_token token_; 141   std::stop_token token_;
124   mutable std::error_code ec_; 142   mutable std::error_code ec_;
125   mutable io_object::implementation* peer_impl_ = nullptr; 143   mutable io_object::implementation* peer_impl_ = nullptr;
126   144  
HITCBC 127   2 accept_awaitable( 145   4 accept_awaitable(
128   local_stream_acceptor& acc, local_stream_socket& peer) noexcept 146   local_stream_acceptor& acc, local_stream_socket& peer) noexcept
HITCBC 129   2 : acc_(acc) 147   4 : acc_(acc)
HITCBC 130   2 , peer_(peer) 148   4 , peer_(peer)
131   { 149   {
HITCBC 132   2 } 150   4 }
133   151  
HITCBC 134   2 bool await_ready() const noexcept 152   4 bool await_ready() const noexcept
135   { 153   {
HITCBC 136   2 return token_.stop_requested(); 154   4 return token_.stop_requested();
137   } 155   }
138   156  
HITCBC 139   2 capy::io_result<> await_resume() const noexcept 157   4 capy::io_result<> await_resume() const noexcept
140   { 158   {
HITCBC 141   2 if (token_.stop_requested()) 159   4 if (token_.stop_requested())
MISUBC 142   return {make_error_code(std::errc::operation_canceled)}; 160   return {make_error_code(std::errc::operation_canceled)};
143   161  
HITCBC 144   2 if (!ec_ && peer_impl_) 162   4 if (!ec_ && peer_impl_)
HITCBC 145   2 peer_.h_.reset(peer_impl_); 163   4 peer_.h_.reset(peer_impl_);
HITCBC 146   2 return {ec_}; 164   4 return {ec_};
147   } 165   }
148   166  
HITCBC 149   2 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 167   4 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
150   -> std::coroutine_handle<> 168   -> std::coroutine_handle<>
151   { 169   {
HITCBC 152   2 token_ = env->stop_token; 170   4 token_ = env->stop_token;
HITCBC 153   6 return acc_.get().accept( 171   12 return acc_.get().accept(
HITCBC 154   6 h, env->executor, token_, &ec_, &peer_impl_); 172   12 h, env->executor, token_, &ec_, &peer_impl_);
155   } 173   }
156   }; 174   };
157   175  
158   public: 176   public:
159   /** Destructor. 177   /** Destructor.
160   178  
161   Closes the acceptor if open, cancelling any pending operations. 179   Closes the acceptor if open, cancelling any pending operations.
162   */ 180   */
163   ~local_stream_acceptor() override; 181   ~local_stream_acceptor() override;
164   182  
165   /** Construct an acceptor from an execution context. 183   /** Construct an acceptor from an execution context.
166   184  
167   @param ctx The execution context that will own this acceptor. 185   @param ctx The execution context that will own this acceptor.
168   */ 186   */
169   explicit local_stream_acceptor(capy::execution_context& ctx); 187   explicit local_stream_acceptor(capy::execution_context& ctx);
170   188  
171   /** Construct an acceptor from an executor. 189   /** Construct an acceptor from an executor.
172   190  
173   The acceptor is associated with the executor's context. 191   The acceptor is associated with the executor's context.
174   192  
175   @param ex The executor whose context will own the acceptor. 193   @param ex The executor whose context will own the acceptor.
176   194  
177   @tparam Ex A type satisfying @ref capy::Executor. Must not 195   @tparam Ex A type satisfying @ref capy::Executor. Must not
178   be `local_stream_acceptor` itself (disables implicit 196   be `local_stream_acceptor` itself (disables implicit
179   conversion from move). 197   conversion from move).
180   */ 198   */
181   template<class Ex> 199   template<class Ex>
182   requires(!std::same_as<std::remove_cvref_t<Ex>, local_stream_acceptor>) && 200   requires(!std::same_as<std::remove_cvref_t<Ex>, local_stream_acceptor>) &&
183   capy::Executor<Ex> 201   capy::Executor<Ex>
184   explicit local_stream_acceptor(Ex const& ex) : local_stream_acceptor(ex.context()) 202   explicit local_stream_acceptor(Ex const& ex) : local_stream_acceptor(ex.context())
185   { 203   {
186   } 204   }
187   205  
188   /** Move constructor. 206   /** Move constructor.
189   207  
190   Transfers ownership of the acceptor resources. 208   Transfers ownership of the acceptor resources.
191   209  
192   @param other The acceptor to move from. 210   @param other The acceptor to move from.
193   211  
194   @pre No awaitables returned by @p other's methods exist. 212   @pre No awaitables returned by @p other's methods exist.
195   @pre The execution context associated with @p other must 213   @pre The execution context associated with @p other must
196   outlive this acceptor. 214   outlive this acceptor.
197   */ 215   */
198   local_stream_acceptor(local_stream_acceptor&& other) noexcept 216   local_stream_acceptor(local_stream_acceptor&& other) noexcept
199   : local_stream_acceptor(other.ctx_, std::move(other)) 217   : local_stream_acceptor(other.ctx_, std::move(other))
200   { 218   {
201   } 219   }
202   220  
203   /** Move assignment operator. 221   /** Move assignment operator.
204   222  
205   Closes any existing acceptor and transfers ownership. 223   Closes any existing acceptor and transfers ownership.
206   Both acceptors must share the same execution context. 224   Both acceptors must share the same execution context.
207   225  
208   @param other The acceptor to move from. 226   @param other The acceptor to move from.
209   227  
210   @return Reference to this acceptor. 228   @return Reference to this acceptor.
211   229  
212   @pre `&ctx_ == &other.ctx_` (same execution context). 230   @pre `&ctx_ == &other.ctx_` (same execution context).
213   @pre No awaitables returned by either `*this` or @p other's 231   @pre No awaitables returned by either `*this` or @p other's
214   methods exist. 232   methods exist.
215   */ 233   */
216   local_stream_acceptor& operator=(local_stream_acceptor&& other) noexcept 234   local_stream_acceptor& operator=(local_stream_acceptor&& other) noexcept
217   { 235   {
218   assert(&ctx_ == &other.ctx_ && 236   assert(&ctx_ == &other.ctx_ &&
219   "move-assign requires the same execution_context"); 237   "move-assign requires the same execution_context");
220   if (this != &other) 238   if (this != &other)
221   { 239   {
222   close(); 240   close();
223   io_object::operator=(std::move(other)); 241   io_object::operator=(std::move(other));
224   } 242   }
225   return *this; 243   return *this;
226   } 244   }
227   245  
228   local_stream_acceptor(local_stream_acceptor const&) = delete; 246   local_stream_acceptor(local_stream_acceptor const&) = delete;
229   local_stream_acceptor& operator=(local_stream_acceptor const&) = delete; 247   local_stream_acceptor& operator=(local_stream_acceptor const&) = delete;
230   248  
231   /** Create the acceptor socket. 249   /** Create the acceptor socket.
232   250  
233   @param proto The protocol. Defaults to local_stream{}. 251   @param proto The protocol. Defaults to local_stream{}.
234   252  
235   @throws std::system_error on failure. 253   @throws std::system_error on failure.
236   */ 254   */
237   void open(local_stream proto = {}); 255   void open(local_stream proto = {});
238   256  
239   /** Bind to a local endpoint. 257   /** Bind to a local endpoint.
240   258  
241   @param ep The local endpoint (path) to bind to. 259   @param ep The local endpoint (path) to bind to.
242   @param opt Bind options. Pass bind_option::unlink_existing 260   @param opt Bind options. Pass bind_option::unlink_existing
243   to unlink the socket path before binding (ignored for 261   to unlink the socket path before binding (ignored for
244   abstract sockets and empty endpoints). 262   abstract sockets and empty endpoints).
245   263  
246   @return An error code on failure, empty on success. 264   @return An error code on failure, empty on success.
247   265  
248   @throws std::logic_error if the acceptor is not open. 266   @throws std::logic_error if the acceptor is not open.
249   */ 267   */
250   [[nodiscard]] std::error_code 268   [[nodiscard]] std::error_code
251   bind(corosio::local_endpoint ep, 269   bind(corosio::local_endpoint ep,
252   bind_option opt = bind_option::none); 270   bind_option opt = bind_option::none);
253   271  
254   /** Start listening for incoming connections. 272   /** Start listening for incoming connections.
255   273  
256   @param backlog The maximum pending connection queue length. 274   @param backlog The maximum pending connection queue length.
257   275  
258   @return An error code on failure, empty on success. 276   @return An error code on failure, empty on success.
259   277  
260   @throws std::logic_error if the acceptor is not open. 278   @throws std::logic_error if the acceptor is not open.
261   */ 279   */
262   [[nodiscard]] std::error_code listen(int backlog = 128); 280   [[nodiscard]] std::error_code listen(int backlog = 128);
263   281  
264   /** Close the acceptor. 282   /** Close the acceptor.
265   283  
266   Cancels any pending accept operations and releases the 284   Cancels any pending accept operations and releases the
267   underlying socket. Has no effect if the acceptor is not 285   underlying socket. Has no effect if the acceptor is not
268   open. 286   open.
269   287  
270   @post is_open() == false 288   @post is_open() == false
271   */ 289   */
272   void close(); 290   void close();
273   291  
274   /// Check if the acceptor has an open socket handle. 292   /// Check if the acceptor has an open socket handle.
HITCBC 275   44 bool is_open() const noexcept 293   54 bool is_open() const noexcept
276   { 294   {
HITCBC 277   44 return h_ && get().is_open(); 295   54 return h_ && get().is_open();
278   } 296   }
279   297  
280   /** Initiate an asynchronous accept into an existing socket. 298   /** Initiate an asynchronous accept into an existing socket.
281   299  
282   Completes when a new connection is available. On success 300   Completes when a new connection is available. On success
283   @p peer is reset to the accepted connection. Only one 301   @p peer is reset to the accepted connection. Only one
284   accept may be in flight at a time. 302   accept may be in flight at a time.
285   303  
286   @param peer The socket to receive the accepted connection. 304   @param peer The socket to receive the accepted connection.
287   305  
288   @par Cancellation 306   @par Cancellation
289   Supports cancellation via stop_token or cancel(). 307   Supports cancellation via stop_token or cancel().
290   On cancellation, yields `capy::cond::canceled` and 308   On cancellation, yields `capy::cond::canceled` and
291   @p peer is not modified. 309   @p peer is not modified.
292   310  
293   @return An awaitable that completes with io_result<>. 311   @return An awaitable that completes with io_result<>.
294   312  
295   @throws std::logic_error if the acceptor is not open. 313   @throws std::logic_error if the acceptor is not open.
296   */ 314   */
HITCBC 297   2 auto accept(local_stream_socket& peer) 315   4 auto accept(local_stream_socket& peer)
298   { 316   {
HITCBC 299   2 if (!is_open()) 317   4 if (!is_open())
MISUBC 300   detail::throw_logic_error("accept: acceptor not listening"); 318   detail::throw_logic_error("accept: acceptor not listening");
HITCBC 301   2 return accept_awaitable(*this, peer); 319   4 return accept_awaitable(*this, peer);
302   } 320   }
303   321  
  322 + /** Wait for an incoming connection or readiness condition.
  323 +
  324 + Suspends until the listen socket is ready in the
  325 + requested direction. For `wait_type::read`, completion
  326 + signals that a subsequent @ref accept will succeed
  327 + without blocking. No connection is consumed.
  328 +
  329 + @param w The wait direction.
  330 +
  331 + @return An awaitable that completes with `io_result<>`.
  332 +
  333 + @par Preconditions
  334 + The acceptor must be listening.
  335 + */
  336 + [[nodiscard]] auto wait(wait_type w)
  337 + {
  338 + if (!is_open())
  339 + detail::throw_logic_error("wait: acceptor not listening");
  340 + return wait_awaitable(*this, w);
  341 + }
  342 +
304   /** Initiate an asynchronous accept, returning the socket. 343   /** Initiate an asynchronous accept, returning the socket.
305   344  
306   Completes when a new connection is available. Only one 345   Completes when a new connection is available. Only one
307   accept may be in flight at a time. 346   accept may be in flight at a time.
308   347  
309   @par Cancellation 348   @par Cancellation
310   Supports cancellation via stop_token or cancel(). 349   Supports cancellation via stop_token or cancel().
311   On cancellation, yields `capy::cond::canceled` with 350   On cancellation, yields `capy::cond::canceled` with
312   a default-constructed socket. 351   a default-constructed socket.
313   352  
314   @return An awaitable that completes with 353   @return An awaitable that completes with
315   io_result<local_stream_socket>. 354   io_result<local_stream_socket>.
316   355  
317   @throws std::logic_error if the acceptor is not open. 356   @throws std::logic_error if the acceptor is not open.
318   */ 357   */
HITCBC 319   2 auto accept() 358   2 auto accept()
320   { 359   {
HITCBC 321   2 if (!is_open()) 360   2 if (!is_open())
MISUBC 322   detail::throw_logic_error("accept: acceptor not listening"); 361   detail::throw_logic_error("accept: acceptor not listening");
HITCBC 323   2 return move_accept_awaitable(*this); 362   2 return move_accept_awaitable(*this);
324   } 363   }
325   364  
326   /** Cancel pending asynchronous accept operations. 365   /** Cancel pending asynchronous accept operations.
327   366  
328   Outstanding accept operations complete with 367   Outstanding accept operations complete with
329   @c capy::cond::canceled. Safe to call when no 368   @c capy::cond::canceled. Safe to call when no
330   operations are pending (no-op). 369   operations are pending (no-op).
331   */ 370   */
332   void cancel(); 371   void cancel();
333   372  
334   /** Release ownership of the native socket handle. 373   /** Release ownership of the native socket handle.
335   374  
336   Deregisters the acceptor from the reactor and cancels 375   Deregisters the acceptor from the reactor and cancels
337   pending operations without closing the descriptor. The 376   pending operations without closing the descriptor. The
338   caller takes ownership of the returned handle. 377   caller takes ownership of the returned handle.
339   378  
340   @return The native handle. 379   @return The native handle.
341   380  
342   @throws std::logic_error if the acceptor is not open. 381   @throws std::logic_error if the acceptor is not open.
343   382  
344   @post is_open() == false 383   @post is_open() == false
345   */ 384   */
346   native_handle_type release(); 385   native_handle_type release();
347   386  
348   /** Return the local endpoint the acceptor is bound to. 387   /** Return the local endpoint the acceptor is bound to.
349   388  
350   Returns a default-constructed (empty) endpoint if the 389   Returns a default-constructed (empty) endpoint if the
351   acceptor is not open or not yet bound. Safe to call in 390   acceptor is not open or not yet bound. Safe to call in
352   any state. 391   any state.
353   */ 392   */
354   corosio::local_endpoint local_endpoint() const noexcept; 393   corosio::local_endpoint local_endpoint() const noexcept;
355   394  
356   /** Set a socket option on the acceptor. 395   /** Set a socket option on the acceptor.
357   396  
358   Applies a type-safe socket option to the underlying socket. 397   Applies a type-safe socket option to the underlying socket.
359   The option type encodes the protocol level and option name. 398   The option type encodes the protocol level and option name.
360   399  
361   @param opt The option to set. 400   @param opt The option to set.
362   401  
363   @tparam Option A socket option type providing static 402   @tparam Option A socket option type providing static
364   `level()` and `name()` members, and `data()` / `size()` 403   `level()` and `name()` members, and `data()` / `size()`
365   accessors. 404   accessors.
366   405  
367   @throws std::logic_error if the acceptor is not open. 406   @throws std::logic_error if the acceptor is not open.
368   @throws std::system_error on failure. 407   @throws std::system_error on failure.
369   */ 408   */
370   template<class Option> 409   template<class Option>
371   void set_option(Option const& opt) 410   void set_option(Option const& opt)
372   { 411   {
373   if (!is_open()) 412   if (!is_open())
374   detail::throw_logic_error("set_option: acceptor not open"); 413   detail::throw_logic_error("set_option: acceptor not open");
375   std::error_code ec = get().set_option( 414   std::error_code ec = get().set_option(
376   Option::level(), Option::name(), opt.data(), opt.size()); 415   Option::level(), Option::name(), opt.data(), opt.size());
377   if (ec) 416   if (ec)
378   detail::throw_system_error(ec, "local_stream_acceptor::set_option"); 417   detail::throw_system_error(ec, "local_stream_acceptor::set_option");
379   } 418   }
380   419  
381   /** Get a socket option from the acceptor. 420   /** Get a socket option from the acceptor.
382   421  
383   Retrieves the current value of a type-safe socket option. 422   Retrieves the current value of a type-safe socket option.
384   423  
385   @return The current option value. 424   @return The current option value.
386   425  
387   @tparam Option A socket option type providing static 426   @tparam Option A socket option type providing static
388   `level()` and `name()` members, and `data()` / `size()` 427   `level()` and `name()` members, and `data()` / `size()`
389   / `resize()` members. 428   / `resize()` members.
390   429  
391   @throws std::logic_error if the acceptor is not open. 430   @throws std::logic_error if the acceptor is not open.
392   @throws std::system_error on failure. 431   @throws std::system_error on failure.
393   */ 432   */
394   template<class Option> 433   template<class Option>
395   Option get_option() const 434   Option get_option() const
396   { 435   {
397   if (!is_open()) 436   if (!is_open())
398   detail::throw_logic_error("get_option: acceptor not open"); 437   detail::throw_logic_error("get_option: acceptor not open");
399   Option opt{}; 438   Option opt{};
400   std::size_t sz = opt.size(); 439   std::size_t sz = opt.size();
401   std::error_code ec = 440   std::error_code ec =
402   get().get_option(Option::level(), Option::name(), opt.data(), &sz); 441   get().get_option(Option::level(), Option::name(), opt.data(), &sz);
403   if (ec) 442   if (ec)
404   detail::throw_system_error(ec, "local_stream_acceptor::get_option"); 443   detail::throw_system_error(ec, "local_stream_acceptor::get_option");
405   opt.resize(sz); 444   opt.resize(sz);
406   return opt; 445   return opt;
407   } 446   }
408   447  
409   /** Backend hooks for local stream acceptor operations. 448   /** Backend hooks for local stream acceptor operations.
410   449  
411   Platform backends derive from this to implement 450   Platform backends derive from this to implement
412   accept, option, and lifecycle management. 451   accept, option, and lifecycle management.
413   */ 452   */
414   struct implementation : io_object::implementation 453   struct implementation : io_object::implementation
415   { 454   {
416   /** Initiate an asynchronous accept. 455   /** Initiate an asynchronous accept.
417   456  
418   On completion the backend sets @p *ec and, on 457   On completion the backend sets @p *ec and, on
419   success, stores a pointer to the new socket 458   success, stores a pointer to the new socket
420   implementation in @p *impl_out. 459   implementation in @p *impl_out.
421   460  
422   @param h Coroutine handle to resume. 461   @param h Coroutine handle to resume.
423   @param ex Executor for dispatching the completion. 462   @param ex Executor for dispatching the completion.
424   @param token Stop token for cancellation. 463   @param token Stop token for cancellation.
425   @param ec Output error code. 464   @param ec Output error code.
426   @param impl_out Output pointer for the accepted socket. 465   @param impl_out Output pointer for the accepted socket.
427   @return Coroutine handle to resume immediately. 466   @return Coroutine handle to resume immediately.
428   */ 467   */
429   virtual std::coroutine_handle<> accept( 468   virtual std::coroutine_handle<> accept(
430   std::coroutine_handle<>, 469   std::coroutine_handle<>,
431   capy::executor_ref, 470   capy::executor_ref,
432   std::stop_token, 471   std::stop_token,
433   std::error_code*, 472   std::error_code*,
434   io_object::implementation**) = 0; 473   io_object::implementation**) = 0;
  474 +
  475 + /** Initiate an asynchronous wait for acceptor readiness.
  476 +
  477 + Completes when the listen socket becomes ready for
  478 + the specified direction. No connection is consumed.
  479 + */
  480 + virtual std::coroutine_handle<> wait(
  481 + std::coroutine_handle<> h,
  482 + capy::executor_ref ex,
  483 + wait_type w,
  484 + std::stop_token token,
  485 + std::error_code* ec) = 0;
435   486  
436   /// Return the cached local endpoint. 487   /// Return the cached local endpoint.
437   virtual corosio::local_endpoint local_endpoint() const noexcept = 0; 488   virtual corosio::local_endpoint local_endpoint() const noexcept = 0;
438   489  
439   /// Return whether the underlying socket is open. 490   /// Return whether the underlying socket is open.
440   virtual bool is_open() const noexcept = 0; 491   virtual bool is_open() const noexcept = 0;
441   492  
442   /// Release and return the native handle without closing. 493   /// Release and return the native handle without closing.
443   virtual native_handle_type release_socket() noexcept = 0; 494   virtual native_handle_type release_socket() noexcept = 0;
444   495  
445   /// Cancel pending accept operations. 496   /// Cancel pending accept operations.
446   virtual void cancel() noexcept = 0; 497   virtual void cancel() noexcept = 0;
447   498  
448   /// Set a raw socket option. 499   /// Set a raw socket option.
449   virtual std::error_code set_option( 500   virtual std::error_code set_option(
450   int level, 501   int level,
451   int optname, 502   int optname,
452   void const* data, 503   void const* data,
453   std::size_t size) noexcept = 0; 504   std::size_t size) noexcept = 0;
454   505  
455   /// Get a raw socket option. 506   /// Get a raw socket option.
456   virtual std::error_code 507   virtual std::error_code
457   get_option(int level, int optname, void* data, std::size_t* size) 508   get_option(int level, int optname, void* data, std::size_t* size)
458   const noexcept = 0; 509   const noexcept = 0;
459   }; 510   };
460   511  
461   protected: 512   protected:
462   local_stream_acceptor(handle h, capy::execution_context& ctx) noexcept 513   local_stream_acceptor(handle h, capy::execution_context& ctx) noexcept
463   : io_object(std::move(h)) 514   : io_object(std::move(h))
464   , ctx_(ctx) 515   , ctx_(ctx)
465   { 516   {
466   } 517   }
467   518  
468   local_stream_acceptor( 519   local_stream_acceptor(
469   capy::execution_context& ctx, local_stream_acceptor&& other) noexcept 520   capy::execution_context& ctx, local_stream_acceptor&& other) noexcept
470   : io_object(std::move(other)) 521   : io_object(std::move(other))
471   , ctx_(ctx) 522   , ctx_(ctx)
472   { 523   {
473   } 524   }
474   525  
HITCBC 475   2 static void reset_peer_impl( 526   2 static void reset_peer_impl(
476   local_stream_socket& peer, io_object::implementation* impl) noexcept 527   local_stream_socket& peer, io_object::implementation* impl) noexcept
477   { 528   {
HITCBC 478   2 if (impl) 529   2 if (impl)
HITCBC 479   2 peer.h_.reset(impl); 530   2 peer.h_.reset(impl);
HITCBC 480   2 } 531   2 }
481   532  
482   private: 533   private:
483   capy::execution_context& ctx_; 534   capy::execution_context& ctx_;
484   535  
HITCBC 485   48 inline implementation& get() const noexcept 536   60 inline implementation& get() const noexcept
486   { 537   {
HITCBC 487   48 return *static_cast<implementation*>(h_.get()); 538   60 return *static_cast<implementation*>(h_.get());
488   } 539   }
489   }; 540   };
490   541  
491   } // namespace boost::corosio 542   } // namespace boost::corosio
492   543  
493   #endif // BOOST_COROSIO_LOCAL_STREAM_ACCEPTOR_HPP 544   #endif // BOOST_COROSIO_LOCAL_STREAM_ACCEPTOR_HPP