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