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