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