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