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