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