97.78% Lines (88/90) 100.00% Functions (22/22)
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/native_handle.hpp> 16   #include <boost/corosio/detail/native_handle.hpp>
17   #include <boost/corosio/detail/op_base.hpp> 17   #include <boost/corosio/detail/op_base.hpp>
18   #include <boost/corosio/wait_type.hpp> 18   #include <boost/corosio/wait_type.hpp>
19   #include <boost/corosio/io/io_object.hpp> 19   #include <boost/corosio/io/io_object.hpp>
20   #include <boost/capy/io_result.hpp> 20   #include <boost/capy/io_result.hpp>
21   #include <boost/corosio/endpoint.hpp> 21   #include <boost/corosio/endpoint.hpp>
22   #include <boost/corosio/tcp.hpp> 22   #include <boost/corosio/tcp.hpp>
23   #include <boost/corosio/tcp_socket.hpp> 23   #include <boost/corosio/tcp_socket.hpp>
24   #include <boost/capy/ex/executor_ref.hpp> 24   #include <boost/capy/ex/executor_ref.hpp>
25   #include <boost/capy/ex/execution_context.hpp> 25   #include <boost/capy/ex/execution_context.hpp>
26   #include <boost/capy/ex/io_env.hpp> 26   #include <boost/capy/ex/io_env.hpp>
27   #include <boost/capy/concept/executor.hpp> 27   #include <boost/capy/concept/executor.hpp>
28   28  
29   #include <system_error> 29   #include <system_error>
30   30  
31   #include <concepts> 31   #include <concepts>
32   #include <coroutine> 32   #include <coroutine>
33   #include <cstddef> 33   #include <cstddef>
34   #include <stop_token> 34   #include <stop_token>
35   #include <type_traits> 35   #include <type_traits>
36   36  
37   namespace boost::corosio { 37   namespace boost::corosio {
38   38  
39   /** An asynchronous TCP acceptor for coroutine I/O. 39   /** An asynchronous TCP acceptor for coroutine I/O.
40   40  
41   This class provides asynchronous TCP accept operations that return 41   This class provides asynchronous TCP accept operations that return
42   awaitable types. The acceptor binds to a local endpoint and listens 42   awaitable types. The acceptor binds to a local endpoint and listens
43   for incoming connections. 43   for incoming connections.
44   44  
45   Each accept operation participates in the affine awaitable protocol, 45   Each accept operation participates in the affine awaitable protocol,
46   ensuring coroutines resume on the correct executor. 46   ensuring coroutines resume on the correct executor.
47   47  
48   @par Thread Safety 48   @par Thread Safety
49   Distinct objects: Safe.@n 49   Distinct objects: Safe.@n
50   Shared objects: Unsafe. An acceptor must not have concurrent accept 50   Shared objects: Unsafe. An acceptor must not have concurrent accept
51   operations. 51   operations.
52   52  
53   @par Semantics 53   @par Semantics
54   Wraps the platform TCP listener. Operations dispatch to 54   Wraps the platform TCP listener. Operations dispatch to
55   OS accept APIs via the io_context reactor. 55   OS accept APIs via the io_context reactor.
56   56  
57   @par Example 57   @par Example
58   @code 58   @code
59   // Convenience constructor: open + configure + bind + listen 59   // Convenience constructor: open + configure + bind + listen
60   io_context ioc; 60   io_context ioc;
61   tcp_acceptor acc( ioc, endpoint( 8080 ) ); 61   tcp_acceptor acc( ioc, endpoint( 8080 ) );
62   62  
63   tcp_socket peer( ioc ); 63   tcp_socket peer( ioc );
64   auto [ec] = co_await acc.accept( peer ); 64   auto [ec] = co_await acc.accept( peer );
65   if ( !ec ) { 65   if ( !ec ) {
66   // peer is now a connected socket 66   // peer is now a connected socket
67   auto [ec2, n] = co_await peer.read_some( buf ); 67   auto [ec2, n] = co_await peer.read_some( buf );
68   } 68   }
69   @endcode 69   @endcode
70   70  
71   @par Example 71   @par Example
72   @code 72   @code
73   // Fine-grained setup 73   // Fine-grained setup
74   tcp_acceptor acc( ioc ); 74   tcp_acceptor acc( ioc );
75   if ( auto ec = acc.open( tcp::v6() ) ) 75   if ( auto ec = acc.open( tcp::v6() ) )
76   return ec; 76   return ec;
77   acc.set_option( socket_option::reuse_address( true ) ); 77   acc.set_option( socket_option::reuse_address( true ) );
78   acc.set_option( socket_option::v6_only( true ) ); 78   acc.set_option( socket_option::v6_only( true ) );
79   if ( auto ec = acc.bind( endpoint( ipv6_address::any(), 8080 ) ) ) 79   if ( auto ec = acc.bind( endpoint( ipv6_address::any(), 8080 ) ) )
80   return ec; 80   return ec;
81   if ( auto ec = acc.listen() ) 81   if ( auto ec = acc.listen() )
82   return ec; 82   return ec;
83   @endcode 83   @endcode
84   */ 84   */
85   class BOOST_COROSIO_DECL tcp_acceptor : public io_object 85   class BOOST_COROSIO_DECL tcp_acceptor : public io_object
86   { 86   {
87   struct wait_awaitable 87   struct wait_awaitable
88   : detail::void_op_base<wait_awaitable> 88   : detail::void_op_base<wait_awaitable>
89   { 89   {
90   tcp_acceptor& acc_; 90   tcp_acceptor& acc_;
91   wait_type w_; 91   wait_type w_;
92   92  
HITCBC 93   19 wait_awaitable(tcp_acceptor& acc, wait_type w) noexcept 93   22 wait_awaitable(tcp_acceptor& acc, wait_type w) noexcept
HITCBC 94   19 : acc_(acc), w_(w) {} 94   22 : acc_(acc), w_(w) {}
95   95  
HITCBC 96   17 std::coroutine_handle<> dispatch( 96   20 std::coroutine_handle<> dispatch(
97   std::coroutine_handle<> h, capy::executor_ref ex) const 97   std::coroutine_handle<> h, capy::executor_ref ex) const
98   { 98   {
HITCBC 99   17 return acc_.get().wait(h, ex, w_, token_, &ec_); 99   20 return acc_.get().wait(h, ex, w_, token_, &ec_);
100   } 100   }
101   }; 101   };
102   102  
103   struct accept_awaitable 103   struct accept_awaitable
104   { 104   {
105   tcp_acceptor& acc_; 105   tcp_acceptor& acc_;
106   tcp_socket& peer_; 106   tcp_socket& peer_;
107   std::stop_token token_; 107   std::stop_token token_;
108   mutable std::error_code ec_; 108   mutable std::error_code ec_;
109   mutable io_object::implementation* peer_impl_ = nullptr; 109   mutable io_object::implementation* peer_impl_ = nullptr;
110   110  
HITCBC 111   4171 accept_awaitable(tcp_acceptor& acc, tcp_socket& peer) noexcept 111   5137 accept_awaitable(tcp_acceptor& acc, tcp_socket& peer) noexcept
HITCBC 112   4171 : acc_(acc) 112   5137 : acc_(acc)
HITCBC 113   4171 , peer_(peer) 113   5137 , peer_(peer)
114   { 114   {
HITCBC 115   4171 } 115   5137 }
116   116  
HITCBC 117   4171 bool await_ready() const noexcept 117   5137 bool await_ready() const noexcept
118   { 118   {
119   // A pre-set ec_ means the initiator failed before 119   // A pre-set ec_ means the initiator failed before
120   // dispatch (e.g. a closed object). 120   // dispatch (e.g. a closed object).
HITCBC 121   4171 return static_cast<bool>(ec_) || token_.stop_requested(); 121   5137 return static_cast<bool>(ec_) || token_.stop_requested();
122   } 122   }
123   123  
HITCBC 124   4169 [[nodiscard]] capy::io_result<> await_resume() const noexcept 124   5127 [[nodiscard]] capy::io_result<> await_resume() const noexcept
125   { 125   {
HITCBC 126   4169 if (token_.stop_requested()) 126   5127 if (token_.stop_requested())
HITCBC 127   27 return {make_error_code(std::errc::operation_canceled)}; 127   27 return {make_error_code(std::errc::operation_canceled)};
128   128  
HITCBC 129   4142 if (!ec_ && peer_impl_) 129   5100 if (!ec_ && peer_impl_)
HITCBC 130   4131 peer_.h_.reset(peer_impl_); 130   5080 peer_.h_.reset(peer_impl_);
HITCBC 131   4142 return {ec_}; 131   5100 return {ec_};
132   } 132   }
133   133  
HITCBC 134   4169 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 134   5135 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
135   -> std::coroutine_handle<> 135   -> std::coroutine_handle<>
136   { 136   {
HITCBC 137   4169 token_ = env->stop_token; 137   5135 token_ = env->stop_token;
HITCBC 138   12507 return acc_.get().accept( 138   15405 return acc_.get().accept(
HITCBC 139   12507 h, env->executor, token_, &ec_, &peer_impl_); 139   15405 h, env->executor, token_, &ec_, &peer_impl_);
140   } 140   }
141   }; 141   };
142   142  
143   struct accept_value_awaitable 143   struct accept_value_awaitable
144   { 144   {
145   tcp_acceptor& acc_; 145   tcp_acceptor& acc_;
146   std::stop_token token_; 146   std::stop_token token_;
147   mutable std::error_code ec_; 147   mutable std::error_code ec_;
148   mutable io_object::implementation* peer_impl_ = nullptr; 148   mutable io_object::implementation* peer_impl_ = nullptr;
149   149  
HITCBC 150   31 explicit accept_value_awaitable(tcp_acceptor& acc) noexcept 150   31 explicit accept_value_awaitable(tcp_acceptor& acc) noexcept
HITCBC 151   31 : acc_(acc) 151   31 : acc_(acc)
152   { 152   {
HITCBC 153   31 } 153   31 }
154   154  
HITCBC 155   31 bool await_ready() const noexcept 155   31 bool await_ready() const noexcept
156   { 156   {
157   // A pre-set ec_ means the initiator failed before 157   // A pre-set ec_ means the initiator failed before
158   // dispatch (e.g. a closed object). 158   // dispatch (e.g. a closed object).
HITCBC 159   31 return static_cast<bool>(ec_) || token_.stop_requested(); 159   31 return static_cast<bool>(ec_) || token_.stop_requested();
160   } 160   }
161   161  
HITCBC 162   31 [[nodiscard]] capy::io_result<tcp_socket> await_resume() noexcept 162   31 [[nodiscard]] capy::io_result<tcp_socket> await_resume() noexcept
163   { 163   {
164   // The peer is built only on success: error paths must not 164   // The peer is built only on success: error paths must not
165   // touch acc_.context(), which a moved-from acceptor lacks. 165   // touch acc_.context(), which a moved-from acceptor lacks.
HITCBC 166   31 if (token_.stop_requested()) 166   31 if (token_.stop_requested())
MISUBC 167   return {make_error_code(std::errc::operation_canceled), 167   return {make_error_code(std::errc::operation_canceled),
MISUBC 168   tcp_socket()}; 168   tcp_socket()};
169   169  
HITCBC 170   31 if (ec_ || !peer_impl_) 170   31 if (ec_ || !peer_impl_)
HITCBC 171   4 return {ec_, tcp_socket()}; 171   4 return {ec_, tcp_socket()};
172   172  
HITCBC 173   27 tcp_socket peer(acc_.context()); 173   27 tcp_socket peer(acc_.context());
HITCBC 174   27 peer.h_.reset(peer_impl_); 174   27 peer.h_.reset(peer_impl_);
HITCBC 175   27 return {ec_, std::move(peer)}; 175   27 return {ec_, std::move(peer)};
HITCBC 176   27 } 176   27 }
177   177  
HITCBC 178   27 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 178   27 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
179   -> std::coroutine_handle<> 179   -> std::coroutine_handle<>
180   { 180   {
HITCBC 181   27 token_ = env->stop_token; 181   27 token_ = env->stop_token;
HITCBC 182   81 return acc_.get().accept( 182   81 return acc_.get().accept(
HITCBC 183   81 h, env->executor, token_, &ec_, &peer_impl_); 183   81 h, env->executor, token_, &ec_, &peer_impl_);
184   } 184   }
185   }; 185   };
186   186  
187   public: 187   public:
188   /** Destructor. 188   /** Destructor.
189   189  
190   Closes the acceptor if open, cancelling any pending operations. 190   Closes the acceptor if open, cancelling any pending operations.
191   */ 191   */
192   ~tcp_acceptor() override; 192   ~tcp_acceptor() override;
193   193  
194   /** Construct an acceptor from an execution context. 194   /** Construct an acceptor from an execution context.
195   195  
196   @param ctx The execution context that will own this acceptor. 196   @param ctx The execution context that will own this acceptor.
197   */ 197   */
198   explicit tcp_acceptor(capy::execution_context& ctx); 198   explicit tcp_acceptor(capy::execution_context& ctx);
199   199  
200   /** Convenience constructor: open + configure + bind + listen. 200   /** Convenience constructor: open + configure + bind + listen.
201   201  
202   Creates a fully-bound listening acceptor in a single 202   Creates a fully-bound listening acceptor in a single
203   expression, throwing the codes the piecewise `open()` + 203   expression, throwing the codes the piecewise `open()` +
204   `set_option()` + `bind()` + `listen()` path reports. The 204   `set_option()` + `bind()` + `listen()` path reports. The
205   address family is deduced from @p ep. 205   address family is deduced from @p ep.
206   206  
207   Before binding, the constructor configures address reuse so 207   Before binding, the constructor configures address reuse so
208   a server can rebind its port immediately after a restart: 208   a server can rebind its port immediately after a restart:
209   `SO_REUSEADDR` on POSIX, `SO_EXCLUSIVEADDRUSE` on Windows 209   `SO_REUSEADDR` on POSIX, `SO_EXCLUSIVEADDRUSE` on Windows
210   ( where `SO_REUSEADDR` instead grants other sockets 210   ( where `SO_REUSEADDR` instead grants other sockets
211   bind-over rights ). A second listener on an occupied 211   bind-over rights ). A second listener on an occupied
212   endpoint therefore throws `errc::address_in_use` on every 212   endpoint therefore throws `errc::address_in_use` on every
213   platform. 213   platform.
214   214  
215   @param ctx The execution context that will own this acceptor. 215   @param ctx The execution context that will own this acceptor.
216   @param ep The local endpoint to bind to. 216   @param ep The local endpoint to bind to.
217   @param backlog The maximum pending connection queue length. 217   @param backlog The maximum pending connection queue length.
218   218  
219   @throws std::system_error on open, configuration, bind, or 219   @throws std::system_error on open, configuration, bind, or
220   listen failure. 220   listen failure.
221   */ 221   */
222   tcp_acceptor(capy::execution_context& ctx, endpoint ep, int backlog = 128); 222   tcp_acceptor(capy::execution_context& ctx, endpoint ep, int backlog = 128);
223   223  
224   /** Construct an acceptor from an executor. 224   /** Construct an acceptor from an executor.
225   225  
226   The acceptor is associated with the executor's context. 226   The acceptor is associated with the executor's context.
227   227  
228   @param ex The executor whose context will own the acceptor. 228   @param ex The executor whose context will own the acceptor.
229   */ 229   */
230   template<class Ex> 230   template<class Ex>
231   requires(!std::same_as<std::remove_cvref_t<Ex>, tcp_acceptor>) && 231   requires(!std::same_as<std::remove_cvref_t<Ex>, tcp_acceptor>) &&
232   capy::Executor<Ex> 232   capy::Executor<Ex>
HITCBC 233   1 explicit tcp_acceptor(Ex const& ex) : tcp_acceptor(ex.context()) 233   1 explicit tcp_acceptor(Ex const& ex) : tcp_acceptor(ex.context())
234   { 234   {
HITCBC 235   1 } 235   1 }
236   236  
237   /** Convenience constructor from an executor. 237   /** Convenience constructor from an executor.
238   238  
239   @param ex The executor whose context will own the acceptor. 239   @param ex The executor whose context will own the acceptor.
240   @param ep The local endpoint to bind to. 240   @param ep The local endpoint to bind to.
241   @param backlog The maximum pending connection queue length. 241   @param backlog The maximum pending connection queue length.
242   242  
243   @throws std::system_error on open, configuration, bind, or 243   @throws std::system_error on open, configuration, bind, or
244   listen failure. 244   listen failure.
245   */ 245   */
246   template<class Ex> 246   template<class Ex>
247   requires capy::Executor<Ex> 247   requires capy::Executor<Ex>
248   tcp_acceptor(Ex const& ex, endpoint ep, int backlog = 128) 248   tcp_acceptor(Ex const& ex, endpoint ep, int backlog = 128)
249   : tcp_acceptor(ex.context(), ep, backlog) 249   : tcp_acceptor(ex.context(), ep, backlog)
250   { 250   {
251   } 251   }
252   252  
253   /** Move constructor. 253   /** Move constructor.
254   254  
255   Transfers ownership of the acceptor resources. 255   Transfers ownership of the acceptor resources.
256   256  
257   @param other The acceptor to move from. 257   @param other The acceptor to move from.
258   258  
259   @pre No awaitables returned by @p other's methods exist. 259   @pre No awaitables returned by @p other's methods exist.
260   @pre The execution context associated with @p other must 260   @pre The execution context associated with @p other must
261   outlive this acceptor. 261   outlive this acceptor.
262   */ 262   */
HITCBC 263   9 tcp_acceptor(tcp_acceptor&& other) noexcept : io_object(std::move(other)) {} 263   9 tcp_acceptor(tcp_acceptor&& other) noexcept : io_object(std::move(other)) {}
264   264  
265   /** Move assignment operator. 265   /** Move assignment operator.
266   266  
267   Closes any existing acceptor and transfers ownership. 267   Closes any existing acceptor and transfers ownership.
268   268  
269   @param other The acceptor to move from. 269   @param other The acceptor to move from.
270   270  
271   @pre No awaitables returned by either `*this` or @p other's 271   @pre No awaitables returned by either `*this` or @p other's
272   methods exist. 272   methods exist.
273   @pre The execution context associated with @p other must 273   @pre The execution context associated with @p other must
274   outlive this acceptor. 274   outlive this acceptor.
275   275  
276   @return Reference to this acceptor. 276   @return Reference to this acceptor.
277   */ 277   */
HITCBC 278   3 tcp_acceptor& operator=(tcp_acceptor&& other) noexcept 278   3 tcp_acceptor& operator=(tcp_acceptor&& other) noexcept
279   { 279   {
HITCBC 280   3 if (this != &other) 280   3 if (this != &other)
281   { 281   {
HITCBC 282   3 close(); 282   3 close();
HITCBC 283   3 h_ = std::move(other.h_); 283   3 h_ = std::move(other.h_);
284   } 284   }
HITCBC 285   3 return *this; 285   3 return *this;
286   } 286   }
287   287  
288   tcp_acceptor(tcp_acceptor const&) = delete; 288   tcp_acceptor(tcp_acceptor const&) = delete;
289   tcp_acceptor& operator=(tcp_acceptor const&) = delete; 289   tcp_acceptor& operator=(tcp_acceptor const&) = delete;
290   290  
291   /** Create the acceptor socket without binding or listening. 291   /** Create the acceptor socket without binding or listening.
292   292  
293   Creates a TCP socket with dual-stack enabled for IPv6. 293   Creates a TCP socket with dual-stack enabled for IPv6.
294   Does not set SO_REUSEADDR — call `set_option` explicitly 294   Does not set SO_REUSEADDR — call `set_option` explicitly
295   if needed. 295   if needed.
296   296  
297   If the acceptor is already open, this function is a no-op. 297   If the acceptor is already open, this function is a no-op.
298   298  
299   Failures such as descriptor exhaustion are normal runtime 299   Failures such as descriptor exhaustion are normal runtime
300   conditions and are reported through the returned error code. 300   conditions and are reported through the returned error code.
301   301  
302   @param proto The protocol (IPv4 or IPv6). Defaults to 302   @param proto The protocol (IPv4 or IPv6). Defaults to
303   `tcp::v4()`. 303   `tcp::v4()`.
304   304  
305   @par Example 305   @par Example
306   @code 306   @code
307   if (auto ec = acc.open( tcp::v6() )) 307   if (auto ec = acc.open( tcp::v6() ))
308   return; // report the error 308   return; // report the error
309   acc.set_option( socket_option::reuse_address( true ) ); 309   acc.set_option( socket_option::reuse_address( true ) );
310   if (auto ec = acc.bind( endpoint( ipv6_address::any(), 8080 ) )) 310   if (auto ec = acc.bind( endpoint( ipv6_address::any(), 8080 ) ))
311   return; 311   return;
312   if (auto ec = acc.listen()) 312   if (auto ec = acc.listen())
313   return; 313   return;
314   @endcode 314   @endcode
315   315  
316   @see bind, listen 316   @see bind, listen
317   317  
318   @return The error code, empty on success. 318   @return The error code, empty on success.
319   */ 319   */
320   [[nodiscard]] std::error_code open(tcp proto = tcp::v4()) noexcept; 320   [[nodiscard]] std::error_code open(tcp proto = tcp::v4()) noexcept;
321   321  
322   /** Bind to a local endpoint. 322   /** Bind to a local endpoint.
323   323  
324   The acceptor must be open. Binds the socket to @p ep and 324   The acceptor must be open. Binds the socket to @p ep and
325   caches the resolved local endpoint (useful when port 0 is 325   caches the resolved local endpoint (useful when port 0 is
326   used to request an ephemeral port). 326   used to request an ephemeral port).
327   327  
328   @param ep The local endpoint to bind to. 328   @param ep The local endpoint to bind to.
329   329  
330   @return An error code indicating success or the reason for 330   @return An error code indicating success or the reason for
331   failure. 331   failure.
332   332  
333   @par Error Conditions 333   @par Error Conditions
334   @li `errc::address_in_use`: The endpoint is already in use. 334   @li `errc::address_in_use`: The endpoint is already in use.
335   @li `errc::address_not_available`: The address is not available 335   @li `errc::address_not_available`: The address is not available
336   on any local interface. 336   on any local interface.
337   @li `errc::permission_denied`: Insufficient privileges to bind 337   @li `errc::permission_denied`: Insufficient privileges to bind
338   to the endpoint (e.g., privileged port). 338   to the endpoint (e.g., privileged port).
339   339  
340   A closed acceptor reports `errc::bad_file_descriptor`. 340   A closed acceptor reports `errc::bad_file_descriptor`.
341   */ 341   */
342   [[nodiscard]] std::error_code bind(endpoint ep) noexcept; 342   [[nodiscard]] std::error_code bind(endpoint ep) noexcept;
343   343  
344   /** Start listening for incoming connections. 344   /** Start listening for incoming connections.
345   345  
346   The acceptor must be open and bound. Registers the acceptor 346   The acceptor must be open and bound. Registers the acceptor
347   with the platform reactor. 347   with the platform reactor.
348   348  
349   @param backlog The maximum length of the queue of pending 349   @param backlog The maximum length of the queue of pending
350   connections. Defaults to 128. 350   connections. Defaults to 128.
351   351  
352   @return An error code indicating success or the reason for 352   @return An error code indicating success or the reason for
353   failure. 353   failure.
354   354  
355   A closed acceptor reports `errc::bad_file_descriptor`. 355   A closed acceptor reports `errc::bad_file_descriptor`.
356   */ 356   */
357   [[nodiscard]] std::error_code listen(int backlog = 128) noexcept; 357   [[nodiscard]] std::error_code listen(int backlog = 128) noexcept;
358   358  
359   /** Close the acceptor. 359   /** Close the acceptor.
360   360  
361   Releases acceptor resources. Any pending operations complete 361   Releases acceptor resources. Any pending operations complete
362   with `errc::operation_canceled`. 362   with `errc::operation_canceled`.
363   */ 363   */
364   void close() noexcept; 364   void close() noexcept;
365   365  
366   /** Check if the acceptor is listening. 366   /** Check if the acceptor is listening.
367   367  
368   @return `true` if the acceptor is open and listening. 368   @return `true` if the acceptor is open and listening.
369   */ 369   */
HITCBC 370   7095 bool is_open() const noexcept 370   8566 bool is_open() const noexcept
371   { 371   {
HITCBC 372   7095 return h_ && get().is_open(); 372   8566 return h_ && get().is_open();
373   } 373   }
374   374  
375   /** Initiate an asynchronous accept operation. 375   /** Initiate an asynchronous accept operation.
376   376  
377   Accepts an incoming connection and initializes the provided 377   Accepts an incoming connection and initializes the provided
378   socket with the new connection. The acceptor must be listening 378   socket with the new connection. The acceptor must be listening
379   before calling this function. 379   before calling this function.
380   380  
381   The operation supports cancellation via `std::stop_token` through 381   The operation supports cancellation via `std::stop_token` through
382   the affine awaitable protocol. If the associated stop token is 382   the affine awaitable protocol. If the associated stop token is
383   triggered, the operation completes immediately with 383   triggered, the operation completes immediately with
384   `errc::operation_canceled`. 384   `errc::operation_canceled`.
385   385  
386   @param peer The socket to receive the accepted connection. Any 386   @param peer The socket to receive the accepted connection. Any
387   existing connection on this socket will be closed. 387   existing connection on this socket will be closed.
388   388  
389   @return An awaitable that completes with `io_result<>`. 389   @return An awaitable that completes with `io_result<>`.
390   Returns success on successful accept, or an error code on 390   Returns success on successful accept, or an error code on
391   failure including: 391   failure including:
392   - operation_canceled: Cancelled via stop_token or cancel(). 392   - operation_canceled: Cancelled via stop_token or cancel().
393   Check `ec == cond::canceled` for portable comparison. 393   Check `ec == cond::canceled` for portable comparison.
394   394  
395   A closed acceptor completes with `errc::bad_file_descriptor`. 395   A closed acceptor completes with `errc::bad_file_descriptor`.
396   396  
397   @par Preconditions 397   @par Preconditions
398   The peer socket must be associated with the same execution context. 398   The peer socket must be associated with the same execution context.
399   399  
400   Both this acceptor and @p peer must outlive the returned 400   Both this acceptor and @p peer must outlive the returned
401   awaitable. 401   awaitable.
402   402  
403   @par Example 403   @par Example
404   @code 404   @code
405   tcp_socket peer(ioc); 405   tcp_socket peer(ioc);
406   auto [ec] = co_await acc.accept(peer); 406   auto [ec] = co_await acc.accept(peer);
407   if (ec) 407   if (ec)
408   co_return; 408   co_return;
409   auto [wec, n] = co_await peer.write_some(buffer); 409   auto [wec, n] = co_await peer.write_some(buffer);
410   @endcode 410   @endcode
411   411  
412   @see accept() 412   @see accept()
413   */ 413   */
HITCBC 414   4171 [[nodiscard]] auto accept(tcp_socket& peer) 414   5137 [[nodiscard]] auto accept(tcp_socket& peer)
415   { 415   {
HITCBC 416   4171 accept_awaitable aw(*this, peer); 416   5137 accept_awaitable aw(*this, peer);
HITCBC 417   4171 if (!is_open()) 417   5137 if (!is_open())
HITCBC 418   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 418   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 419   4171 return aw; 419   5137 return aw;
420   } 420   }
421   421  
422   /** Initiate an asynchronous accept operation, returning the peer. 422   /** Initiate an asynchronous accept operation, returning the peer.
423   423  
424   Accepts an incoming connection and returns a newly constructed 424   Accepts an incoming connection and returns a newly constructed
425   socket for it, associated with this acceptor's execution context. 425   socket for it, associated with this acceptor's execution context.
426   The acceptor must be listening before calling this function. 426   The acceptor must be listening before calling this function.
427   427  
428   The caller does not pre-construct the peer socket; the returned 428   The caller does not pre-construct the peer socket; the returned
429   socket shares this acceptor's execution context. 429   socket shares this acceptor's execution context.
430   430  
431   The operation supports cancellation via `std::stop_token` through 431   The operation supports cancellation via `std::stop_token` through
432   the affine awaitable protocol. If the associated stop token is 432   the affine awaitable protocol. If the associated stop token is
433   triggered, the operation completes immediately with 433   triggered, the operation completes immediately with
434   `errc::operation_canceled`. 434   `errc::operation_canceled`.
435   435  
436   @return An awaitable that completes with `io_result<tcp_socket>`. 436   @return An awaitable that completes with `io_result<tcp_socket>`.
437   On success the payload is the connected peer socket; on failure 437   On success the payload is the connected peer socket; on failure
438   (including cancellation) the error code is set and the payload 438   (including cancellation) the error code is set and the payload
439   socket is unconnected. Errors include: 439   socket is unconnected. Errors include:
440   - operation_canceled: Cancelled via stop_token or cancel(). 440   - operation_canceled: Cancelled via stop_token or cancel().
441   Check `ec == cond::canceled` for portable comparison. 441   Check `ec == cond::canceled` for portable comparison.
442   442  
443   A closed acceptor completes with `errc::bad_file_descriptor`. 443   A closed acceptor completes with `errc::bad_file_descriptor`.
444   On failure the returned socket is default-constructed and 444   On failure the returned socket is default-constructed and
445   may only be destroyed or assigned. 445   may only be destroyed or assigned.
446   446  
447   @par Preconditions 447   @par Preconditions
448   This acceptor must outlive the returned awaitable. 448   This acceptor must outlive the returned awaitable.
449   449  
450   @par Example 450   @par Example
451   @code 451   @code
452   auto [ec, peer] = co_await acc.accept(); 452   auto [ec, peer] = co_await acc.accept();
453   if (ec) 453   if (ec)
454   co_return; 454   co_return;
455   auto [wec, n] = co_await peer.write_some(buffer); 455   auto [wec, n] = co_await peer.write_some(buffer);
456   @endcode 456   @endcode
457   457  
458   @see accept(tcp_socket&) 458   @see accept(tcp_socket&)
459   */ 459   */
HITCBC 460   31 [[nodiscard]] auto accept() 460   31 [[nodiscard]] auto accept()
461   { 461   {
HITCBC 462   31 accept_value_awaitable aw(*this); 462   31 accept_value_awaitable aw(*this);
HITCBC 463   31 if (!is_open()) 463   31 if (!is_open())
HITCBC 464   4 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 464   4 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 465   31 return aw; 465   31 return aw;
466   } 466   }
467   467  
468   /** Wait for an incoming connection or readiness condition. 468   /** Wait for an incoming connection or readiness condition.
469   469  
470   Suspends until the listen socket is ready in the 470   Suspends until the listen socket is ready in the
471   requested direction, or an error condition is reported. 471   requested direction, or an error condition is reported.
472   For `wait_type::read`, completion signals that a 472   For `wait_type::read`, completion signals that a
473   subsequent @ref accept will succeed without blocking; a 473   subsequent @ref accept will succeed without blocking; a
474   connection already queued when the wait begins completes 474   connection already queued when the wait begins completes
475   it immediately. No connection is consumed. 475   it immediately. No connection is consumed.
476   476  
477   @note `wait_type::write` is not usable on an acceptor: 477   @note `wait_type::write` is not usable on an acceptor:
478   writability carries no meaning for a listening socket, so 478   writability carries no meaning for a listening socket, so
479   the wait fails with `errc::operation_not_supported` on 479   the wait fails with `errc::operation_not_supported` on
480   every backend. 480   every backend.
481   481  
482   @param w The wait direction. 482   @param w The wait direction.
483   483  
484   @return An awaitable that completes with `io_result<>`. 484   @return An awaitable that completes with `io_result<>`.
485   485  
486   A closed acceptor completes with `errc::bad_file_descriptor`. 486   A closed acceptor completes with `errc::bad_file_descriptor`.
487   487  
488   @par Preconditions 488   @par Preconditions
489   This acceptor must outlive the returned awaitable. 489   This acceptor must outlive the returned awaitable.
490   */ 490   */
HITCBC 491   19 [[nodiscard]] auto wait(wait_type w) 491   22 [[nodiscard]] auto wait(wait_type w)
492   { 492   {
HITCBC 493   19 wait_awaitable aw(*this, w); 493   22 wait_awaitable aw(*this, w);
HITCBC 494   19 if (!is_open()) 494   22 if (!is_open())
HITCBC 495   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 495   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 496   19 return aw; 496   22 return aw;
497   } 497   }
498   498  
499   /** Cancel any pending asynchronous operations. 499   /** Cancel any pending asynchronous operations.
500   500  
501   All outstanding operations complete with `errc::operation_canceled`. 501   All outstanding operations complete with `errc::operation_canceled`.
502   Check `ec == cond::canceled` for portable comparison. 502   Check `ec == cond::canceled` for portable comparison.
503   */ 503   */
504   void cancel() noexcept; 504   void cancel() noexcept;
505   505  
506   /** Get the native socket handle. 506   /** Get the native socket handle.
507   507  
508   Returns the underlying platform-specific socket descriptor. 508   Returns the underlying platform-specific socket descriptor.
509   On POSIX systems this is an `int` file descriptor. 509   On POSIX systems this is an `int` file descriptor.
510   On Windows this is a `SOCKET` handle. 510   On Windows this is a `SOCKET` handle.
511   511  
512   @return The native socket handle, or -1/INVALID_SOCKET if not open. 512   @return The native socket handle, or -1/INVALID_SOCKET if not open.
513   513  
514   @par Preconditions 514   @par Preconditions
515   None. May be called on closed acceptors. 515   None. May be called on closed acceptors.
516   */ 516   */
517   native_handle_type native_handle() const noexcept; 517   native_handle_type native_handle() const noexcept;
518   518  
519   /** Assign an existing native socket to this acceptor. 519   /** Assign an existing native socket to this acceptor.
520   520  
521   Adopts a listening socket created outside the library — 521   Adopts a listening socket created outside the library —
522   received from a service manager, inherited, or made natively — 522   received from a service manager, inherited, or made natively —
523   and registers it with the backend. The socket must be a 523   and registers it with the backend. The socket must be a
524   listening stream socket in the `AF_INET` or `AF_INET6` family. 524   listening stream socket in the `AF_INET` or `AF_INET6` family.
525   Adoption never alters the descriptor's flags or options: on 525   Adoption never alters the descriptor's flags or options: on
526   POSIX the fd must already be non-blocking, and on Windows the 526   POSIX the fd must already be non-blocking, and on Windows the
527   socket must be overlapped-capable. 527   socket must be overlapped-capable.
528   528  
529   Adoption does not verify listen state; @ref accept reports the 529   Adoption does not verify listen state; @ref accept reports the
530   error if the socket is not listening. 530   error if the socket is not listening.
531   531  
532   If this object is already open, pending operations complete 532   If this object is already open, pending operations complete
533   with `errc::operation_canceled` and the held socket is 533   with `errc::operation_canceled` and the held socket is
534   closed before the new one is adopted. 534   closed before the new one is adopted.
535   535  
536   @par Exception Safety 536   @par Exception Safety
537   Strong guarantee on validation failure: the object is 537   Strong guarantee on validation failure: the object is
538   unchanged. If backend registration fails, the object either 538   unchanged. If backend registration fails, the object either
539   retains its previous socket or is left closed, depending on 539   retains its previous socket or is left closed, depending on
540   the backend. In all failure cases the caller retains 540   the backend. In all failure cases the caller retains
541   ownership of `fd`. 541   ownership of `fd`.
542   542  
543   @param fd The native socket to adopt. On success the object 543   @param fd The native socket to adopt. On success the object
544   owns it and will close it. 544   owns it and will close it.
545   545  
546   @return The error code, empty on success. Validation and 546   @return The error code, empty on success. Validation and
547   registration failures are normal runtime conditions when 547   registration failures are normal runtime conditions when
548   adopting foreign descriptors. 548   adopting foreign descriptors.
549   */ 549   */
550   [[nodiscard]] std::error_code assign(native_handle_type fd) noexcept; 550   [[nodiscard]] std::error_code assign(native_handle_type fd) noexcept;
551   551  
552   /** Release ownership of the native socket handle. 552   /** Release ownership of the native socket handle.
553   553  
554   Deregisters the socket from the backend and cancels pending 554   Deregisters the socket from the backend and cancels pending
555   operations without closing the descriptor. The caller takes 555   operations without closing the descriptor. The caller takes
556   ownership of the returned handle. 556   ownership of the returned handle.
557   557  
558   @return The native handle. 558   @return The native handle.
559   559  
560   @throws std::system_error `errc::bad_file_descriptor` if the 560   @throws std::system_error `errc::bad_file_descriptor` if the
561   acceptor is not open. 561   acceptor is not open.
562   562  
563   @post is_open() == false 563   @post is_open() == false
564   */ 564   */
565   native_handle_type release(); 565   native_handle_type release();
566   566  
567   /** Get the local endpoint of the acceptor. 567   /** Get the local endpoint of the acceptor.
568   568  
569   Returns the local address and port to which the acceptor is bound. 569   Returns the local address and port to which the acceptor is bound.
570   This is useful when binding to port 0 (ephemeral port) to discover 570   This is useful when binding to port 0 (ephemeral port) to discover
571   the OS-assigned port number. The endpoint is cached when bind() 571   the OS-assigned port number. The endpoint is cached when bind()
572   is called. 572   is called.
573   573  
574   @return The local endpoint, or a default endpoint (0.0.0.0:0) if 574   @return The local endpoint, or a default endpoint (0.0.0.0:0) if
575   the acceptor is not open. 575   the acceptor is not open.
576   576  
577   @par Thread Safety 577   @par Thread Safety
578   The cached endpoint value is set during bind() and cleared 578   The cached endpoint value is set during bind() and cleared
579   during close(). This function may be called concurrently with 579   during close(). This function may be called concurrently with
580   accept operations, but must not be called concurrently with 580   accept operations, but must not be called concurrently with
581   bind() or close(). 581   bind() or close().
582   */ 582   */
583   endpoint local_endpoint() const noexcept; 583   endpoint local_endpoint() const noexcept;
584   584  
585   /** Set a socket option on the acceptor. 585   /** Set a socket option on the acceptor.
586   586  
587   Applies a type-safe socket option to the underlying listening 587   Applies a type-safe socket option to the underlying listening
588   socket. The socket must be open (via `open()` or `listen()`). 588   socket. The socket must be open (via `open()` or `listen()`).
589   This is useful for setting options between `open()` and 589   This is useful for setting options between `open()` and
590   `listen()`, such as `socket_option::reuse_port`. 590   `listen()`, such as `socket_option::reuse_port`.
591   591  
592   @par Example 592   @par Example
593   @code 593   @code
594   if ( auto ec = acc.open( tcp::v6() ) ) 594   if ( auto ec = acc.open( tcp::v6() ) )
595   return ec; 595   return ec;
596   acc.set_option( socket_option::reuse_port( true ) ); 596   acc.set_option( socket_option::reuse_port( true ) );
597   if ( auto ec = acc.bind( endpoint( ipv6_address::any(), 8080 ) ) ) 597   if ( auto ec = acc.bind( endpoint( ipv6_address::any(), 8080 ) ) )
598   return ec; 598   return ec;
599   if ( auto ec = acc.listen() ) 599   if ( auto ec = acc.listen() )
600   return ec; 600   return ec;
601   @endcode 601   @endcode
602   602  
603   @param opt The option to set. 603   @param opt The option to set.
604   604  
605   @throws std::system_error `errc::bad_file_descriptor` if the 605   @throws std::system_error `errc::bad_file_descriptor` if the
606   acceptor is not open; otherwise thrown on failure. 606   acceptor is not open; otherwise thrown on failure.
607   */ 607   */
608   template<class Option> 608   template<class Option>
HITCBC 609   404 void set_option(Option const& opt) 609   478 void set_option(Option const& opt)
610   { 610   {
HITCBC 611   404 if (!is_open()) 611   478 if (!is_open())
HITCBC 612   2 detail::throw_system_error( 612   2 detail::throw_system_error(
HITCBC 613   4 make_error_code(std::errc::bad_file_descriptor), 613   4 make_error_code(std::errc::bad_file_descriptor),
614   "tcp_acceptor::set_option"); 614   "tcp_acceptor::set_option");
HITCBC 615   402 std::error_code ec = get().set_option( 615   476 std::error_code ec = get().set_option(
616   Option::level(), Option::name(), opt.data(), opt.size()); 616   Option::level(), Option::name(), opt.data(), opt.size());
HITCBC 617   402 if (ec) 617   476 if (ec)
HITCBC 618   2 detail::throw_system_error(ec, "tcp_acceptor::set_option"); 618   8 detail::throw_system_error(ec, "tcp_acceptor::set_option");
HITCBC 619   400 } 619   468 }
620   620  
621   /** Get a socket option from the acceptor. 621   /** Get a socket option from the acceptor.
622   622  
623   Retrieves the current value of a type-safe socket option. 623   Retrieves the current value of a type-safe socket option.
624   624  
625   @par Example 625   @par Example
626   @code 626   @code
627   auto opt = acc.get_option<socket_option::reuse_address>(); 627   auto opt = acc.get_option<socket_option::reuse_address>();
628   @endcode 628   @endcode
629   629  
630   @return The current option value. 630   @return The current option value.
631   631  
632   @throws std::system_error `errc::bad_file_descriptor` if the 632   @throws std::system_error `errc::bad_file_descriptor` if the
633   acceptor is not open; otherwise thrown on failure. 633   acceptor is not open; otherwise thrown on failure.
634   */ 634   */
635   template<class Option> 635   template<class Option>
HITCBC 636   10 Option get_option() const 636   23 Option get_option() const
637   { 637   {
HITCBC 638   10 if (!is_open()) 638   23 if (!is_open())
HITCBC 639   2 detail::throw_system_error( 639   2 detail::throw_system_error(
HITCBC 640   4 make_error_code(std::errc::bad_file_descriptor), 640   4 make_error_code(std::errc::bad_file_descriptor),
641   "tcp_acceptor::get_option"); 641   "tcp_acceptor::get_option");
HITCBC 642   8 Option opt{}; 642   21 Option opt{};
HITCBC 643   8 std::size_t sz = opt.size(); 643   21 std::size_t sz = opt.size();
644   std::error_code ec = 644   std::error_code ec =
HITCBC 645   8 get().get_option(Option::level(), Option::name(), opt.data(), &sz); 645   21 get().get_option(Option::level(), Option::name(), opt.data(), &sz);
HITCBC 646   8 if (ec) 646   21 if (ec)
HITCBC 647   2 detail::throw_system_error(ec, "tcp_acceptor::get_option"); 647   8 detail::throw_system_error(ec, "tcp_acceptor::get_option");
HITCBC 648   6 opt.resize(sz); 648   13 opt.resize(sz);
HITCBC 649   6 return opt; 649   13 return opt;
650   } 650   }
651   651  
652   /** Define backend hooks for TCP acceptor operations. 652   /** Define backend hooks for TCP acceptor operations.
653   653  
654   Platform backends derive from this to implement 654   Platform backends derive from this to implement
655   accept, endpoint query, open-state checks, cancellation, 655   accept, endpoint query, open-state checks, cancellation,
656   and socket-option management. 656   and socket-option management.
657   */ 657   */
658   struct implementation : io_object::implementation 658   struct implementation : io_object::implementation
659   { 659   {
660   /// Initiate an asynchronous accept operation. 660   /// Initiate an asynchronous accept operation.
661   virtual std::coroutine_handle<> accept( 661   virtual std::coroutine_handle<> accept(
662   std::coroutine_handle<>, 662   std::coroutine_handle<>,
663   capy::executor_ref, 663   capy::executor_ref,
664   std::stop_token, 664   std::stop_token,
665   std::error_code*, 665   std::error_code*,
666   io_object::implementation**) = 0; 666   io_object::implementation**) = 0;
667   667  
668   /** Initiate an asynchronous wait for acceptor readiness. 668   /** Initiate an asynchronous wait for acceptor readiness.
669   669  
670   Completes when the listen socket becomes ready for 670   Completes when the listen socket becomes ready for
671   the specified direction (typically `wait_type::read` 671   the specified direction (typically `wait_type::read`
672   for an incoming connection), or an error condition is 672   for an incoming connection), or an error condition is
673   reported. No connection is consumed. 673   reported. No connection is consumed.
674   */ 674   */
675   virtual std::coroutine_handle<> wait( 675   virtual std::coroutine_handle<> wait(
676   std::coroutine_handle<> h, 676   std::coroutine_handle<> h,
677   capy::executor_ref ex, 677   capy::executor_ref ex,
678   wait_type w, 678   wait_type w,
679   std::stop_token token, 679   std::stop_token token,
680   std::error_code* ec) = 0; 680   std::error_code* ec) = 0;
681   681  
682   /// Returns the cached local endpoint. 682   /// Returns the cached local endpoint.
683   virtual endpoint local_endpoint() const noexcept = 0; 683   virtual endpoint local_endpoint() const noexcept = 0;
684   684  
685   /// Return true if the acceptor has a kernel resource open. 685   /// Return true if the acceptor has a kernel resource open.
686   virtual bool is_open() const noexcept = 0; 686   virtual bool is_open() const noexcept = 0;
687   687  
688   /// Return the native handle, or the platform sentinel if closed. 688   /// Return the native handle, or the platform sentinel if closed.
689   virtual native_handle_type native_handle() const noexcept = 0; 689   virtual native_handle_type native_handle() const noexcept = 0;
690   690  
691   /// Release and return the native handle without closing. 691   /// Release and return the native handle without closing.
692   virtual native_handle_type release_socket() noexcept = 0; 692   virtual native_handle_type release_socket() noexcept = 0;
693   693  
694   /** Cancel any pending asynchronous operations. 694   /** Cancel any pending asynchronous operations.
695   695  
696   All outstanding operations complete with operation_canceled error. 696   All outstanding operations complete with operation_canceled error.
697   */ 697   */
698   virtual void cancel() noexcept = 0; 698   virtual void cancel() noexcept = 0;
699   699  
700   /** Set a socket option. 700   /** Set a socket option.
701   701  
702   @param level The protocol level. 702   @param level The protocol level.
703   @param optname The option name. 703   @param optname The option name.
704   @param data Pointer to the option value. 704   @param data Pointer to the option value.
705   @param size Size of the option value in bytes. 705   @param size Size of the option value in bytes.
706   @return Error code on failure, empty on success. 706   @return Error code on failure, empty on success.
707   */ 707   */
708   virtual std::error_code set_option( 708   virtual std::error_code set_option(
709   int level, 709   int level,
710   int optname, 710   int optname,
711   void const* data, 711   void const* data,
712   std::size_t size) noexcept = 0; 712   std::size_t size) noexcept = 0;
713   713  
714   /** Get a socket option. 714   /** Get a socket option.
715   715  
716   @param level The protocol level. 716   @param level The protocol level.
717   @param optname The option name. 717   @param optname The option name.
718   @param data Pointer to receive the option value. 718   @param data Pointer to receive the option value.
719   @param size On entry, the size of the buffer. On exit, 719   @param size On entry, the size of the buffer. On exit,
720   the size of the option value. 720   the size of the option value.
721   @return Error code on failure, empty on success. 721   @return Error code on failure, empty on success.
722   */ 722   */
723   virtual std::error_code 723   virtual std::error_code
724   get_option(int level, int optname, void* data, std::size_t* size) 724   get_option(int level, int optname, void* data, std::size_t* size)
725   const noexcept = 0; 725   const noexcept = 0;
726   }; 726   };
727   727  
728   protected: 728   protected:
HITCBC 729   27 explicit tcp_acceptor(handle h) noexcept : io_object(std::move(h)) {} 729   27 explicit tcp_acceptor(handle h) noexcept : io_object(std::move(h)) {}
730   730  
731   /// Transfer accepted peer impl to the peer socket. 731   /// Transfer accepted peer impl to the peer socket.
732   static void 732   static void
HITCBC 733   11 reset_peer_impl(tcp_socket& peer, io_object::implementation* impl) noexcept 733   11 reset_peer_impl(tcp_socket& peer, io_object::implementation* impl) noexcept
734   { 734   {
HITCBC 735   11 if (impl) 735   11 if (impl)
HITCBC 736   11 peer.h_.reset(impl); 736   11 peer.h_.reset(impl);
HITCBC 737   11 } 737   11 }
738   738  
739   private: 739   private:
HITCBC 740   12104 inline implementation& get() const noexcept 740   14696 inline implementation& get() const noexcept
741   { 741   {
HITCBC 742   12104 return *static_cast<implementation*>(h_.get()); 742   14696 return *static_cast<implementation*>(h_.get());
743   } 743   }
744   }; 744   };
745   745  
746   } // namespace boost::corosio 746   } // namespace boost::corosio
747   747  
748   #endif 748   #endif