92.00% Lines (46/50) 100.00% Functions (15/15)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com) 2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3   // Copyright (c) 2026 Steve Gerbino 3   // Copyright (c) 2026 Steve Gerbino
4   // 4   //
5   // Distributed under the Boost Software License, Version 1.0. (See accompanying 5   // Distributed under the Boost Software License, Version 1.0. (See accompanying
6   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7   // 7   //
8   // Official repository: https://github.com/cppalliance/corosio 8   // Official repository: https://github.com/cppalliance/corosio
9   // 9   //
10   10  
11   #ifndef BOOST_COROSIO_TCP_ACCEPTOR_HPP 11   #ifndef BOOST_COROSIO_TCP_ACCEPTOR_HPP
12   #define BOOST_COROSIO_TCP_ACCEPTOR_HPP 12   #define BOOST_COROSIO_TCP_ACCEPTOR_HPP
13   13  
14   #include <boost/corosio/detail/config.hpp> 14   #include <boost/corosio/detail/config.hpp>
15   #include <boost/corosio/detail/except.hpp> 15   #include <boost/corosio/detail/except.hpp>
  16 + #include <boost/corosio/detail/op_base.hpp>
  17 + #include <boost/corosio/wait_type.hpp>
16   #include <boost/corosio/io/io_object.hpp> 18   #include <boost/corosio/io/io_object.hpp>
17   #include <boost/capy/io_result.hpp> 19   #include <boost/capy/io_result.hpp>
18   #include <boost/corosio/endpoint.hpp> 20   #include <boost/corosio/endpoint.hpp>
19   #include <boost/corosio/tcp.hpp> 21   #include <boost/corosio/tcp.hpp>
20   #include <boost/corosio/tcp_socket.hpp> 22   #include <boost/corosio/tcp_socket.hpp>
21   #include <boost/capy/ex/executor_ref.hpp> 23   #include <boost/capy/ex/executor_ref.hpp>
22   #include <boost/capy/ex/execution_context.hpp> 24   #include <boost/capy/ex/execution_context.hpp>
23   #include <boost/capy/ex/io_env.hpp> 25   #include <boost/capy/ex/io_env.hpp>
24   #include <boost/capy/concept/executor.hpp> 26   #include <boost/capy/concept/executor.hpp>
25   27  
26   #include <system_error> 28   #include <system_error>
27   29  
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   /** An asynchronous TCP acceptor for coroutine I/O. 38   /** An asynchronous TCP acceptor for coroutine I/O.
37   39  
38   This class provides asynchronous TCP accept operations that return 40   This class provides asynchronous TCP accept operations that return
39   awaitable types. The acceptor binds to a local endpoint and listens 41   awaitable types. The acceptor binds to a local endpoint and listens
40   for incoming connections. 42   for incoming connections.
41   43  
42   Each accept operation participates in the affine awaitable protocol, 44   Each accept operation participates in the affine awaitable protocol,
43   ensuring coroutines resume on the correct executor. 45   ensuring coroutines resume on the correct executor.
44   46  
45   @par Thread Safety 47   @par Thread Safety
46   Distinct objects: Safe.@n 48   Distinct objects: Safe.@n
47   Shared objects: Unsafe. An acceptor must not have concurrent accept 49   Shared objects: Unsafe. An acceptor must not have concurrent accept
48   operations. 50   operations.
49   51  
50   @par Semantics 52   @par Semantics
51   Wraps the platform TCP listener. Operations dispatch to 53   Wraps the platform TCP listener. Operations dispatch to
52   OS accept APIs via the io_context reactor. 54   OS accept APIs via the io_context reactor.
53   55  
54   @par Example 56   @par Example
55   @code 57   @code
56   // Convenience constructor: open + SO_REUSEADDR + bind + listen 58   // Convenience constructor: open + SO_REUSEADDR + bind + listen
57   io_context ioc; 59   io_context ioc;
58   tcp_acceptor acc( ioc, endpoint( 8080 ) ); 60   tcp_acceptor acc( ioc, endpoint( 8080 ) );
59   61  
60   tcp_socket peer( ioc ); 62   tcp_socket peer( ioc );
61   auto [ec] = co_await acc.accept( peer ); 63   auto [ec] = co_await acc.accept( peer );
62   if ( !ec ) { 64   if ( !ec ) {
63   // peer is now a connected socket 65   // peer is now a connected socket
64   auto [ec2, n] = co_await peer.read_some( buf ); 66   auto [ec2, n] = co_await peer.read_some( buf );
65   } 67   }
66   @endcode 68   @endcode
67   69  
68   @par Example 70   @par Example
69   @code 71   @code
70   // Fine-grained setup 72   // Fine-grained setup
71   tcp_acceptor acc( ioc ); 73   tcp_acceptor acc( ioc );
72   acc.open( tcp::v6() ); 74   acc.open( tcp::v6() );
73   acc.set_option( socket_option::reuse_address( true ) ); 75   acc.set_option( socket_option::reuse_address( true ) );
74   acc.set_option( socket_option::v6_only( true ) ); 76   acc.set_option( socket_option::v6_only( true ) );
75   if ( auto ec = acc.bind( endpoint( ipv6_address::any(), 8080 ) ) ) 77   if ( auto ec = acc.bind( endpoint( ipv6_address::any(), 8080 ) ) )
76   return ec; 78   return ec;
77   if ( auto ec = acc.listen() ) 79   if ( auto ec = acc.listen() )
78   return ec; 80   return ec;
79   @endcode 81   @endcode
80   */ 82   */
81   class BOOST_COROSIO_DECL tcp_acceptor : public io_object 83   class BOOST_COROSIO_DECL tcp_acceptor : public io_object
82   { 84   {
  85 + struct wait_awaitable
  86 + : detail::void_op_base<wait_awaitable>
  87 + {
  88 + tcp_acceptor& acc_;
  89 + wait_type w_;
  90 +
HITGNC   91 + 2 wait_awaitable(tcp_acceptor& acc, wait_type w) noexcept
HITGNC   92 + 2 : acc_(acc), w_(w) {}
  93 +
HITGNC   94 + 2 std::coroutine_handle<> dispatch(
  95 + std::coroutine_handle<> h, capy::executor_ref ex) const
  96 + {
HITGNC   97 + 2 return acc_.get().wait(h, ex, w_, token_, &ec_);
  98 + }
  99 + };
  100 +
83   struct accept_awaitable 101   struct accept_awaitable
84   { 102   {
85   tcp_acceptor& acc_; 103   tcp_acceptor& acc_;
86   tcp_socket& peer_; 104   tcp_socket& peer_;
87   std::stop_token token_; 105   std::stop_token token_;
88   mutable std::error_code ec_; 106   mutable std::error_code ec_;
89   mutable io_object::implementation* peer_impl_ = nullptr; 107   mutable io_object::implementation* peer_impl_ = nullptr;
90   108  
HITCBC 91   8723 accept_awaitable(tcp_acceptor& acc, tcp_socket& peer) noexcept 109   8415 accept_awaitable(tcp_acceptor& acc, tcp_socket& peer) noexcept
HITCBC 92   8723 : acc_(acc) 110   8415 : acc_(acc)
HITCBC 93   8723 , peer_(peer) 111   8415 , peer_(peer)
94   { 112   {
HITCBC 95   8723 } 113   8415 }
96   114  
HITCBC 97   8723 bool await_ready() const noexcept 115   8415 bool await_ready() const noexcept
98   { 116   {
HITCBC 99   8723 return token_.stop_requested(); 117   8415 return token_.stop_requested();
100   } 118   }
101   119  
HITCBC 102   8723 capy::io_result<> await_resume() const noexcept 120   8415 capy::io_result<> await_resume() const noexcept
103   { 121   {
HITCBC 104   8723 if (token_.stop_requested()) 122   8415 if (token_.stop_requested())
HITCBC 105   6 return {make_error_code(std::errc::operation_canceled)}; 123   6 return {make_error_code(std::errc::operation_canceled)};
106   124  
HITCBC 107   8717 if (!ec_ && peer_impl_) 125   8409 if (!ec_ && peer_impl_)
HITCBC 108   8711 peer_.h_.reset(peer_impl_); 126   8403 peer_.h_.reset(peer_impl_);
HITCBC 109   8717 return {ec_}; 127   8409 return {ec_};
110   } 128   }
111   129  
HITCBC 112   8723 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 130   8415 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
113   -> std::coroutine_handle<> 131   -> std::coroutine_handle<>
114   { 132   {
HITCBC 115   8723 token_ = env->stop_token; 133   8415 token_ = env->stop_token;
HITCBC 116   26169 return acc_.get().accept( 134   25245 return acc_.get().accept(
HITCBC 117   26169 h, env->executor, token_, &ec_, &peer_impl_); 135   25245 h, env->executor, token_, &ec_, &peer_impl_);
118   } 136   }
119   }; 137   };
120   138  
121   public: 139   public:
122   /** Destructor. 140   /** Destructor.
123   141  
124   Closes the acceptor if open, cancelling any pending operations. 142   Closes the acceptor if open, cancelling any pending operations.
125   */ 143   */
126   ~tcp_acceptor() override; 144   ~tcp_acceptor() override;
127   145  
128   /** Construct an acceptor from an execution context. 146   /** Construct an acceptor from an execution context.
129   147  
130   @param ctx The execution context that will own this acceptor. 148   @param ctx The execution context that will own this acceptor.
131   */ 149   */
132   explicit tcp_acceptor(capy::execution_context& ctx); 150   explicit tcp_acceptor(capy::execution_context& ctx);
133   151  
134   /** Convenience constructor: open + SO_REUSEADDR + bind + listen. 152   /** Convenience constructor: open + SO_REUSEADDR + bind + listen.
135   153  
136   Creates a fully-bound listening acceptor in a single 154   Creates a fully-bound listening acceptor in a single
137   expression. The address family is deduced from @p ep. 155   expression. The address family is deduced from @p ep.
138   156  
139   @param ctx The execution context that will own this acceptor. 157   @param ctx The execution context that will own this acceptor.
140   @param ep The local endpoint to bind to. 158   @param ep The local endpoint to bind to.
141   @param backlog The maximum pending connection queue length. 159   @param backlog The maximum pending connection queue length.
142   160  
143   @throws std::system_error on bind or listen failure. 161   @throws std::system_error on bind or listen failure.
144   */ 162   */
145   tcp_acceptor(capy::execution_context& ctx, endpoint ep, int backlog = 128); 163   tcp_acceptor(capy::execution_context& ctx, endpoint ep, int backlog = 128);
146   164  
147   /** Construct an acceptor from an executor. 165   /** Construct an acceptor from an executor.
148   166  
149   The acceptor is associated with the executor's context. 167   The acceptor is associated with the executor's context.
150   168  
151   @param ex The executor whose context will own the acceptor. 169   @param ex The executor whose context will own the acceptor.
152   */ 170   */
153   template<class Ex> 171   template<class Ex>
154   requires(!std::same_as<std::remove_cvref_t<Ex>, tcp_acceptor>) && 172   requires(!std::same_as<std::remove_cvref_t<Ex>, tcp_acceptor>) &&
155   capy::Executor<Ex> 173   capy::Executor<Ex>
156   explicit tcp_acceptor(Ex const& ex) : tcp_acceptor(ex.context()) 174   explicit tcp_acceptor(Ex const& ex) : tcp_acceptor(ex.context())
157   { 175   {
158   } 176   }
159   177  
160   /** Convenience constructor from an executor. 178   /** Convenience constructor from an executor.
161   179  
162   @param ex The executor whose context will own the acceptor. 180   @param ex The executor whose context will own the acceptor.
163   @param ep The local endpoint to bind to. 181   @param ep The local endpoint to bind to.
164   @param backlog The maximum pending connection queue length. 182   @param backlog The maximum pending connection queue length.
165   183  
166   @throws std::system_error on bind or listen failure. 184   @throws std::system_error on bind or listen failure.
167   */ 185   */
168   template<class Ex> 186   template<class Ex>
169   requires capy::Executor<Ex> 187   requires capy::Executor<Ex>
170   tcp_acceptor(Ex const& ex, endpoint ep, int backlog = 128) 188   tcp_acceptor(Ex const& ex, endpoint ep, int backlog = 128)
171   : tcp_acceptor(ex.context(), ep, backlog) 189   : tcp_acceptor(ex.context(), ep, backlog)
172   { 190   {
173   } 191   }
174   192  
175   /** Move constructor. 193   /** Move constructor.
176   194  
177   Transfers ownership of the acceptor resources. 195   Transfers ownership of the acceptor resources.
178   196  
179   @param other The acceptor to move from. 197   @param other The acceptor to move from.
180   198  
181   @pre No awaitables returned by @p other's methods exist. 199   @pre No awaitables returned by @p other's methods exist.
182   @pre The execution context associated with @p other must 200   @pre The execution context associated with @p other must
183   outlive this acceptor. 201   outlive this acceptor.
184   */ 202   */
HITCBC 185   2 tcp_acceptor(tcp_acceptor&& other) noexcept : io_object(std::move(other)) {} 203   2 tcp_acceptor(tcp_acceptor&& other) noexcept : io_object(std::move(other)) {}
186   204  
187   /** Move assignment operator. 205   /** Move assignment operator.
188   206  
189   Closes any existing acceptor and transfers ownership. 207   Closes any existing acceptor and transfers ownership.
190   208  
191   @param other The acceptor to move from. 209   @param other The acceptor to move from.
192   210  
193   @pre No awaitables returned by either `*this` or @p other's 211   @pre No awaitables returned by either `*this` or @p other's
194   methods exist. 212   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   @return Reference to this acceptor. 216   @return Reference to this acceptor.
199   */ 217   */
HITCBC 200   2 tcp_acceptor& operator=(tcp_acceptor&& other) noexcept 218   2 tcp_acceptor& operator=(tcp_acceptor&& other) noexcept
201   { 219   {
HITCBC 202   2 if (this != &other) 220   2 if (this != &other)
203   { 221   {
HITCBC 204   2 close(); 222   2 close();
HITCBC 205   2 h_ = std::move(other.h_); 223   2 h_ = std::move(other.h_);
206   } 224   }
HITCBC 207   2 return *this; 225   2 return *this;
208   } 226   }
209   227  
210   tcp_acceptor(tcp_acceptor const&) = delete; 228   tcp_acceptor(tcp_acceptor const&) = delete;
211   tcp_acceptor& operator=(tcp_acceptor const&) = delete; 229   tcp_acceptor& operator=(tcp_acceptor const&) = delete;
212   230  
213   /** Create the acceptor socket without binding or listening. 231   /** Create the acceptor socket without binding or listening.
214   232  
215   Creates a TCP socket with dual-stack enabled for IPv6. 233   Creates a TCP socket with dual-stack enabled for IPv6.
216   Does not set SO_REUSEADDR — call `set_option` explicitly 234   Does not set SO_REUSEADDR — call `set_option` explicitly
217   if needed. 235   if needed.
218   236  
219   If the acceptor is already open, this function is a no-op. 237   If the acceptor is already open, this function is a no-op.
220   238  
221   @param proto The protocol (IPv4 or IPv6). Defaults to 239   @param proto The protocol (IPv4 or IPv6). Defaults to
222   `tcp::v4()`. 240   `tcp::v4()`.
223   241  
224   @throws std::system_error on failure. 242   @throws std::system_error on failure.
225   243  
226   @par Example 244   @par Example
227   @code 245   @code
228   acc.open( tcp::v6() ); 246   acc.open( tcp::v6() );
229   acc.set_option( socket_option::reuse_address( true ) ); 247   acc.set_option( socket_option::reuse_address( true ) );
230   acc.bind( endpoint( ipv6_address::any(), 8080 ) ); 248   acc.bind( endpoint( ipv6_address::any(), 8080 ) );
231   acc.listen(); 249   acc.listen();
232   @endcode 250   @endcode
233   251  
234   @see bind, listen 252   @see bind, listen
235   */ 253   */
236   void open(tcp proto = tcp::v4()); 254   void open(tcp proto = tcp::v4());
237   255  
238   /** Bind to a local endpoint. 256   /** Bind to a local endpoint.
239   257  
240   The acceptor must be open. Binds the socket to @p ep and 258   The acceptor must be open. Binds the socket to @p ep and
241   caches the resolved local endpoint (useful when port 0 is 259   caches the resolved local endpoint (useful when port 0 is
242   used to request an ephemeral port). 260   used to request an ephemeral port).
243   261  
244   @param ep The local endpoint to bind to. 262   @param ep The local endpoint to bind to.
245   263  
246   @return An error code indicating success or the reason for 264   @return An error code indicating success or the reason for
247   failure. 265   failure.
248   266  
249   @par Error Conditions 267   @par Error Conditions
250   @li `errc::address_in_use`: The endpoint is already in use. 268   @li `errc::address_in_use`: The endpoint is already in use.
251   @li `errc::address_not_available`: The address is not available 269   @li `errc::address_not_available`: The address is not available
252   on any local interface. 270   on any local interface.
253   @li `errc::permission_denied`: Insufficient privileges to bind 271   @li `errc::permission_denied`: Insufficient privileges to bind
254   to the endpoint (e.g., privileged port). 272   to the endpoint (e.g., privileged port).
255   273  
256   @throws std::logic_error if the acceptor is not open. 274   @throws std::logic_error if the acceptor is not open.
257   */ 275   */
258   [[nodiscard]] std::error_code bind(endpoint ep); 276   [[nodiscard]] std::error_code bind(endpoint ep);
259   277  
260   /** Start listening for incoming connections. 278   /** Start listening for incoming connections.
261   279  
262   The acceptor must be open and bound. Registers the acceptor 280   The acceptor must be open and bound. Registers the acceptor
263   with the platform reactor. 281   with the platform reactor.
264   282  
265   @param backlog The maximum length of the queue of pending 283   @param backlog The maximum length of the queue of pending
266   connections. Defaults to 128. 284   connections. Defaults to 128.
267   285  
268   @return An error code indicating success or the reason for 286   @return An error code indicating success or the reason for
269   failure. 287   failure.
270   288  
271   @throws std::logic_error if the acceptor is not open. 289   @throws std::logic_error if the acceptor is not open.
272   */ 290   */
273   [[nodiscard]] std::error_code listen(int backlog = 128); 291   [[nodiscard]] std::error_code listen(int backlog = 128);
274   292  
275   /** Close the acceptor. 293   /** Close the acceptor.
276   294  
277   Releases acceptor resources. Any pending operations complete 295   Releases acceptor resources. Any pending operations complete
278   with `errc::operation_canceled`. 296   with `errc::operation_canceled`.
279   */ 297   */
280   void close(); 298   void close();
281   299  
282   /** Check if the acceptor is listening. 300   /** Check if the acceptor is listening.
283   301  
284   @return `true` if the acceptor is open and listening. 302   @return `true` if the acceptor is open and listening.
285   */ 303   */
HITCBC 286   9893 bool is_open() const noexcept 304   9641 bool is_open() const noexcept
287   { 305   {
HITCBC 288   9893 return h_ && get().is_open(); 306   9641 return h_ && get().is_open();
289   } 307   }
290   308  
291   /** Initiate an asynchronous accept operation. 309   /** Initiate an asynchronous accept operation.
292   310  
293   Accepts an incoming connection and initializes the provided 311   Accepts an incoming connection and initializes the provided
294   socket with the new connection. The acceptor must be listening 312   socket with the new connection. The acceptor must be listening
295   before calling this function. 313   before calling this function.
296   314  
297   The operation supports cancellation via `std::stop_token` through 315   The operation supports cancellation via `std::stop_token` through
298   the affine awaitable protocol. If the associated stop token is 316   the affine awaitable protocol. If the associated stop token is
299   triggered, the operation completes immediately with 317   triggered, the operation completes immediately with
300   `errc::operation_canceled`. 318   `errc::operation_canceled`.
301   319  
302   @param peer The socket to receive the accepted connection. Any 320   @param peer The socket to receive the accepted connection. Any
303   existing connection on this socket will be closed. 321   existing connection on this socket will be closed.
304   322  
305   @return An awaitable that completes with `io_result<>`. 323   @return An awaitable that completes with `io_result<>`.
306   Returns success on successful accept, or an error code on 324   Returns success on successful accept, or an error code on
307   failure including: 325   failure including:
308   - operation_canceled: Cancelled via stop_token or cancel(). 326   - operation_canceled: Cancelled via stop_token or cancel().
309   Check `ec == cond::canceled` for portable comparison. 327   Check `ec == cond::canceled` for portable comparison.
310   328  
311   @par Preconditions 329   @par Preconditions
312   The acceptor must be listening (`is_open() == true`). 330   The acceptor must be listening (`is_open() == true`).
313   The peer socket must be associated with the same execution context. 331   The peer socket must be associated with the same execution context.
314   332  
315   Both this acceptor and @p peer must outlive the returned 333   Both this acceptor and @p peer must outlive the returned
316   awaitable. 334   awaitable.
317   335  
318   @par Example 336   @par Example
319   @code 337   @code
320   tcp_socket peer(ioc); 338   tcp_socket peer(ioc);
321   auto [ec] = co_await acc.accept(peer); 339   auto [ec] = co_await acc.accept(peer);
322   if (!ec) { 340   if (!ec) {
323   // Use peer socket 341   // Use peer socket
324   } 342   }
325   @endcode 343   @endcode
326   */ 344   */
HITCBC 327   8723 auto accept(tcp_socket& peer) 345   8415 auto accept(tcp_socket& peer)
328   { 346   {
HITCBC 329   8723 if (!is_open()) 347   8415 if (!is_open())
MISUBC 330   detail::throw_logic_error("accept: acceptor not listening"); 348   detail::throw_logic_error("accept: acceptor not listening");
HITCBC 331   8723 return accept_awaitable(*this, peer); 349   8415 return accept_awaitable(*this, peer);
332   } 350   }
333   351  
  352 + /** Wait for an incoming connection or readiness condition.
  353 +
  354 + Suspends until the listen socket is ready in the
  355 + requested direction, or an error condition is reported.
  356 + For `wait_type::read`, completion signals that a
  357 + subsequent @ref accept will succeed without blocking.
  358 + No connection is consumed.
  359 +
  360 + @param w The wait direction.
  361 +
  362 + @return An awaitable that completes with `io_result<>`.
  363 +
  364 + @par Preconditions
  365 + The acceptor must be listening. This acceptor must
  366 + outlive the returned awaitable.
  367 + */
HITGNC   368 + 2 [[nodiscard]] auto wait(wait_type w)
  369 + {
HITGNC   370 + 2 if (!is_open())
MISUNC   371 + detail::throw_logic_error("wait: acceptor not listening");
HITGNC   372 + 2 return wait_awaitable(*this, w);
  373 + }
  374 +
334   /** Cancel any pending asynchronous operations. 375   /** Cancel any pending asynchronous operations.
335   376  
336   All outstanding operations complete with `errc::operation_canceled`. 377   All outstanding operations complete with `errc::operation_canceled`.
337   Check `ec == cond::canceled` for portable comparison. 378   Check `ec == cond::canceled` for portable comparison.
338   */ 379   */
339   void cancel(); 380   void cancel();
340   381  
341   /** Get the local endpoint of the acceptor. 382   /** Get the local endpoint of the acceptor.
342   383  
343   Returns the local address and port to which the acceptor is bound. 384   Returns the local address and port to which the acceptor is bound.
344   This is useful when binding to port 0 (ephemeral port) to discover 385   This is useful when binding to port 0 (ephemeral port) to discover
345   the OS-assigned port number. The endpoint is cached when listen() 386   the OS-assigned port number. The endpoint is cached when listen()
346   is called. 387   is called.
347   388  
348   @return The local endpoint, or a default endpoint (0.0.0.0:0) if 389   @return The local endpoint, or a default endpoint (0.0.0.0:0) if
349   the acceptor is not listening. 390   the acceptor is not listening.
350   391  
351   @par Thread Safety 392   @par Thread Safety
352   The cached endpoint value is set during listen() and cleared 393   The cached endpoint value is set during listen() and cleared
353   during close(). This function may be called concurrently with 394   during close(). This function may be called concurrently with
354   accept operations, but must not be called concurrently with 395   accept operations, but must not be called concurrently with
355   listen() or close(). 396   listen() or close().
356   */ 397   */
357   endpoint local_endpoint() const noexcept; 398   endpoint local_endpoint() const noexcept;
358   399  
359   /** Set a socket option on the acceptor. 400   /** Set a socket option on the acceptor.
360   401  
361   Applies a type-safe socket option to the underlying listening 402   Applies a type-safe socket option to the underlying listening
362   socket. The socket must be open (via `open()` or `listen()`). 403   socket. The socket must be open (via `open()` or `listen()`).
363   This is useful for setting options between `open()` and 404   This is useful for setting options between `open()` and
364   `listen()`, such as `socket_option::reuse_port`. 405   `listen()`, such as `socket_option::reuse_port`.
365   406  
366   @par Example 407   @par Example
367   @code 408   @code
368   acc.open( tcp::v6() ); 409   acc.open( tcp::v6() );
369   acc.set_option( socket_option::reuse_port( true ) ); 410   acc.set_option( socket_option::reuse_port( true ) );
370   acc.bind( endpoint( ipv6_address::any(), 8080 ) ); 411   acc.bind( endpoint( ipv6_address::any(), 8080 ) );
371   acc.listen(); 412   acc.listen();
372   @endcode 413   @endcode
373   414  
374   @param opt The option to set. 415   @param opt The option to set.
375   416  
376   @throws std::logic_error if the acceptor is not open. 417   @throws std::logic_error if the acceptor is not open.
377   @throws std::system_error on failure. 418   @throws std::system_error on failure.
378   */ 419   */
379   template<class Option> 420   template<class Option>
HITCBC 380   163 void set_option(Option const& opt) 421   171 void set_option(Option const& opt)
381   { 422   {
HITCBC 382   163 if (!is_open()) 423   171 if (!is_open())
MISUBC 383   detail::throw_logic_error("set_option: acceptor not open"); 424   detail::throw_logic_error("set_option: acceptor not open");
HITCBC 384   163 std::error_code ec = get().set_option( 425   171 std::error_code ec = get().set_option(
385   Option::level(), Option::name(), opt.data(), opt.size()); 426   Option::level(), Option::name(), opt.data(), opt.size());
HITCBC 386   163 if (ec) 427   171 if (ec)
MISUBC 387   detail::throw_system_error(ec, "tcp_acceptor::set_option"); 428   detail::throw_system_error(ec, "tcp_acceptor::set_option");
HITCBC 388   163 } 429   171 }
389   430  
390   /** Get a socket option from the acceptor. 431   /** Get a socket option from the acceptor.
391   432  
392   Retrieves the current value of a type-safe socket option. 433   Retrieves the current value of a type-safe socket option.
393   434  
394   @par Example 435   @par Example
395   @code 436   @code
396   auto opt = acc.get_option<socket_option::reuse_address>(); 437   auto opt = acc.get_option<socket_option::reuse_address>();
397   @endcode 438   @endcode
398   439  
399   @return The current option value. 440   @return The current option value.
400   441  
401   @throws std::logic_error if the acceptor is not open. 442   @throws std::logic_error if the acceptor is not open.
402   @throws std::system_error on failure. 443   @throws std::system_error on failure.
403   */ 444   */
404   template<class Option> 445   template<class Option>
405   Option get_option() const 446   Option get_option() const
406   { 447   {
407   if (!is_open()) 448   if (!is_open())
408   detail::throw_logic_error("get_option: acceptor not open"); 449   detail::throw_logic_error("get_option: acceptor not open");
409   Option opt{}; 450   Option opt{};
410   std::size_t sz = opt.size(); 451   std::size_t sz = opt.size();
411   std::error_code ec = 452   std::error_code ec =
412   get().get_option(Option::level(), Option::name(), opt.data(), &sz); 453   get().get_option(Option::level(), Option::name(), opt.data(), &sz);
413   if (ec) 454   if (ec)
414   detail::throw_system_error(ec, "tcp_acceptor::get_option"); 455   detail::throw_system_error(ec, "tcp_acceptor::get_option");
415   opt.resize(sz); 456   opt.resize(sz);
416   return opt; 457   return opt;
417   } 458   }
418   459  
419   /** Define backend hooks for TCP acceptor operations. 460   /** Define backend hooks for TCP acceptor operations.
420   461  
421   Platform backends derive from this to implement 462   Platform backends derive from this to implement
422   accept, endpoint query, open-state checks, cancellation, 463   accept, endpoint query, open-state checks, cancellation,
423   and socket-option management. 464   and socket-option management.
424   */ 465   */
425   struct implementation : io_object::implementation 466   struct implementation : io_object::implementation
426   { 467   {
427   /// Initiate an asynchronous accept operation. 468   /// Initiate an asynchronous accept operation.
428   virtual std::coroutine_handle<> accept( 469   virtual std::coroutine_handle<> accept(
429   std::coroutine_handle<>, 470   std::coroutine_handle<>,
430   capy::executor_ref, 471   capy::executor_ref,
431   std::stop_token, 472   std::stop_token,
432   std::error_code*, 473   std::error_code*,
433   io_object::implementation**) = 0; 474   io_object::implementation**) = 0;
  475 +
  476 + /** Initiate an asynchronous wait for acceptor readiness.
  477 +
  478 + Completes when the listen socket becomes ready for
  479 + the specified direction (typically `wait_type::read`
  480 + for an incoming connection), or an error condition is
  481 + reported. No connection is consumed.
  482 + */
  483 + virtual std::coroutine_handle<> wait(
  484 + std::coroutine_handle<> h,
  485 + capy::executor_ref ex,
  486 + wait_type w,
  487 + std::stop_token token,
  488 + std::error_code* ec) = 0;
434   489  
435   /// Returns the cached local endpoint. 490   /// Returns the cached local endpoint.
436   virtual endpoint local_endpoint() const noexcept = 0; 491   virtual endpoint local_endpoint() const noexcept = 0;
437   492  
438   /// Return true if the acceptor has a kernel resource open. 493   /// Return true if the acceptor has a kernel resource open.
439   virtual bool is_open() const noexcept = 0; 494   virtual bool is_open() const noexcept = 0;
440   495  
441   /** Cancel any pending asynchronous operations. 496   /** Cancel any pending asynchronous operations.
442   497  
443   All outstanding operations complete with operation_canceled error. 498   All outstanding operations complete with operation_canceled error.
444   */ 499   */
445   virtual void cancel() noexcept = 0; 500   virtual void cancel() noexcept = 0;
446   501  
447   /** Set a socket option. 502   /** Set a socket option.
448   503  
449   @param level The protocol level. 504   @param level The protocol level.
450   @param optname The option name. 505   @param optname The option name.
451   @param data Pointer to the option value. 506   @param data Pointer to the option value.
452   @param size Size of the option value in bytes. 507   @param size Size of the option value in bytes.
453   @return Error code on failure, empty on success. 508   @return Error code on failure, empty on success.
454   */ 509   */
455   virtual std::error_code set_option( 510   virtual std::error_code set_option(
456   int level, 511   int level,
457   int optname, 512   int optname,
458   void const* data, 513   void const* data,
459   std::size_t size) noexcept = 0; 514   std::size_t size) noexcept = 0;
460   515  
461   /** Get a socket option. 516   /** Get a socket option.
462   517  
463   @param level The protocol level. 518   @param level The protocol level.
464   @param optname The option name. 519   @param optname The option name.
465   @param data Pointer to receive the option value. 520   @param data Pointer to receive the option value.
466   @param size On entry, the size of the buffer. On exit, 521   @param size On entry, the size of the buffer. On exit,
467   the size of the option value. 522   the size of the option value.
468   @return Error code on failure, empty on success. 523   @return Error code on failure, empty on success.
469   */ 524   */
470   virtual std::error_code 525   virtual std::error_code
471   get_option(int level, int optname, void* data, std::size_t* size) 526   get_option(int level, int optname, void* data, std::size_t* size)
472   const noexcept = 0; 527   const noexcept = 0;
473   }; 528   };
474   529  
475   protected: 530   protected:
HITCBC 476   4 explicit tcp_acceptor(handle h) noexcept : io_object(std::move(h)) {} 531   4 explicit tcp_acceptor(handle h) noexcept : io_object(std::move(h)) {}
477   532  
478   /// Transfer accepted peer impl to the peer socket. 533   /// Transfer accepted peer impl to the peer socket.
479   static void 534   static void
HITCBC 480   4 reset_peer_impl(tcp_socket& peer, io_object::implementation* impl) noexcept 535   4 reset_peer_impl(tcp_socket& peer, io_object::implementation* impl) noexcept
481   { 536   {
HITCBC 482   4 if (impl) 537   4 if (impl)
HITCBC 483   4 peer.h_.reset(impl); 538   4 peer.h_.reset(impl);
HITCBC 484   4 } 539   4 }
485   540  
486   private: 541   private:
HITCBC 487   18931 inline implementation& get() const noexcept 542   18389 inline implementation& get() const noexcept
488   { 543   {
HITCBC 489   18931 return *static_cast<implementation*>(h_.get()); 544   18389 return *static_cast<implementation*>(h_.get());
490   } 545   }
491   }; 546   };
492   547  
493   } // namespace boost::corosio 548   } // namespace boost::corosio
494   549  
495   #endif 550   #endif