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