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