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