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