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