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