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