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