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