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