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