100.00% Lines (57/57) 100.00% Functions (18/18)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2026 Steve Gerbino 2   // Copyright (c) 2026 Steve Gerbino
3   // Copyright (c) 2026 Michael Vandeberg 3   // Copyright (c) 2026 Michael Vandeberg
4   // 4   //
5   // Distributed under the Boost Software License, Version 1.0. (See accompanying 5   // Distributed under the Boost Software License, Version 1.0. (See accompanying
6   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7   // 7   //
8   // Official repository: https://github.com/cppalliance/corosio 8   // Official repository: https://github.com/cppalliance/corosio
9   // 9   //
10   10  
11   #ifndef BOOST_COROSIO_NATIVE_NATIVE_TCP_ACCEPTOR_HPP 11   #ifndef BOOST_COROSIO_NATIVE_NATIVE_TCP_ACCEPTOR_HPP
12   #define BOOST_COROSIO_NATIVE_NATIVE_TCP_ACCEPTOR_HPP 12   #define BOOST_COROSIO_NATIVE_NATIVE_TCP_ACCEPTOR_HPP
13   13  
14   #include <boost/corosio/tcp_acceptor.hpp> 14   #include <boost/corosio/tcp_acceptor.hpp>
15   #include <boost/corosio/backend.hpp> 15   #include <boost/corosio/backend.hpp>
16   #include <boost/corosio/detail/op_base.hpp> 16   #include <boost/corosio/detail/op_base.hpp>
17   17  
18   #ifndef BOOST_COROSIO_MRDOCS 18   #ifndef BOOST_COROSIO_MRDOCS
19   #if BOOST_COROSIO_HAS_EPOLL 19   #if BOOST_COROSIO_HAS_EPOLL
20   #include <boost/corosio/native/detail/epoll/epoll_types.hpp> 20   #include <boost/corosio/native/detail/epoll/epoll_types.hpp>
21   #endif 21   #endif
22   22  
23   #if BOOST_COROSIO_HAS_SELECT 23   #if BOOST_COROSIO_HAS_SELECT
24   #include <boost/corosio/native/detail/select/select_types.hpp> 24   #include <boost/corosio/native/detail/select/select_types.hpp>
25   #endif 25   #endif
26   26  
27   #if BOOST_COROSIO_HAS_KQUEUE 27   #if BOOST_COROSIO_HAS_KQUEUE
28   #include <boost/corosio/native/detail/kqueue/kqueue_types.hpp> 28   #include <boost/corosio/native/detail/kqueue/kqueue_types.hpp>
29   #endif 29   #endif
30   30  
31   #if BOOST_COROSIO_HAS_IOCP 31   #if BOOST_COROSIO_HAS_IOCP
32   #include <boost/corosio/native/detail/iocp/win_tcp_acceptor_service.hpp> 32   #include <boost/corosio/native/detail/iocp/win_tcp_acceptor_service.hpp>
33   #endif 33   #endif
34   34  
35   #if BOOST_COROSIO_HAS_URING 35   #if BOOST_COROSIO_HAS_URING
36   #include <boost/corosio/native/detail/uring/uring_types.hpp> 36   #include <boost/corosio/native/detail/uring/uring_types.hpp>
37   #endif 37   #endif
38   #endif // !BOOST_COROSIO_MRDOCS 38   #endif // !BOOST_COROSIO_MRDOCS
39   39  
40   namespace boost::corosio { 40   namespace boost::corosio {
41   41  
42   /** Accepts TCP connections, calling the backend directly. 42   /** Accepts TCP connections, calling the backend directly.
43   43  
44   This class template inherits from @ref tcp_acceptor. It shadows the 44   This class template inherits from @ref tcp_acceptor. It shadows the
45   `accept` operation with a version that calls the backend 45   `accept` operation with a version that calls the backend
46   implementation directly. The compiler can then inline through the 46   implementation directly. The compiler can then inline through the
47   entire call chain. 47   entire call chain.
48   48  
49   Non-async operations (`listen`, `close`, `cancel`) remain 49   Non-async operations (`listen`, `close`, `cancel`) remain
50   unchanged and dispatch through the compiled library. 50   unchanged and dispatch through the compiled library.
51   51  
52   A `native_tcp_acceptor` IS-A `tcp_acceptor` and can be passed 52   A `native_tcp_acceptor` IS-A `tcp_acceptor` and can be passed
53   to any function expecting `tcp_acceptor&`. 53   to any function expecting `tcp_acceptor&`.
54   54  
55   @tparam Backend A backend tag value (e.g., `epoll`). 55   @tparam Backend A backend tag value (e.g., `epoll`).
56   56  
57   @par Thread Safety 57   @par Thread Safety
58   Same as @ref tcp_acceptor. 58   Same as @ref tcp_acceptor.
59   59  
60   @see @ref tcp_acceptor, @ref epoll_t, @ref iocp_t 60   @see @ref tcp_acceptor, @ref epoll_t, @ref iocp_t
61   */ 61   */
62   template<auto Backend> 62   template<auto Backend>
63   class native_tcp_acceptor : public tcp_acceptor 63   class native_tcp_acceptor : public tcp_acceptor
64   { 64   {
65   using backend_type = decltype(Backend); 65   using backend_type = decltype(Backend);
66   using impl_type = typename backend_type::tcp_acceptor_type; 66   using impl_type = typename backend_type::tcp_acceptor_type;
67   using service_type = typename backend_type::tcp_acceptor_service_type; 67   using service_type = typename backend_type::tcp_acceptor_service_type;
68   68  
HITCBC 69   21 impl_type& get_impl() noexcept 69   25 impl_type& get_impl() noexcept
70   { 70   {
HITCBC 71   21 return *static_cast<impl_type*>(h_.get()); 71   25 return *static_cast<impl_type*>(h_.get());
72   } 72   }
73   73  
74   struct native_wait_awaitable : detail::void_op_base<native_wait_awaitable> 74   struct native_wait_awaitable : detail::void_op_base<native_wait_awaitable>
75   { 75   {
76   native_tcp_acceptor& acc_; 76   native_tcp_acceptor& acc_;
77   wait_type w_; 77   wait_type w_;
78   78  
HITCBC 79   6 native_wait_awaitable(native_tcp_acceptor& acc, wait_type w) noexcept 79   6 native_wait_awaitable(native_tcp_acceptor& acc, wait_type w) noexcept
HITCBC 80   6 : acc_(acc) 80   6 : acc_(acc)
HITCBC 81   6 , w_(w) 81   6 , w_(w)
82   { 82   {
HITCBC 83   6 } 83   6 }
84   84  
85   std::coroutine_handle<> 85   std::coroutine_handle<>
HITCBC 86   4 dispatch(capy::continuation& cont, capy::executor_ref ex) const 86   4 dispatch(capy::continuation& cont, capy::executor_ref ex) const
87   { 87   {
HITCBC 88   4 return acc_.get_impl().wait(cont, ex, w_, this->token_, &this->ec_); 88   4 return acc_.get_impl().wait(cont, ex, w_, this->token_, &this->ec_);
89   } 89   }
90   }; 90   };
91   91  
92   struct native_accept_awaitable 92   struct native_accept_awaitable
93   : detail::void_op_base<native_accept_awaitable> 93   : detail::void_op_base<native_accept_awaitable>
94   { 94   {
95   native_tcp_acceptor& acc_; 95   native_tcp_acceptor& acc_;
96   tcp_socket& peer_; 96   tcp_socket& peer_;
97   mutable io_object::implementation* peer_impl_ = nullptr; 97   mutable io_object::implementation* peer_impl_ = nullptr;
98   98  
HITCBC 99   19 native_accept_awaitable( 99   21 native_accept_awaitable(
100   native_tcp_acceptor& acc, tcp_socket& peer) noexcept 100   native_tcp_acceptor& acc, tcp_socket& peer) noexcept
HITCBC 101   19 : acc_(acc) 101   21 : acc_(acc)
HITCBC 102   19 , peer_(peer) 102   21 , peer_(peer)
103   { 103   {
HITCBC 104   19 } 104   21 }
105   105  
  106 + // A peer accepted for an awaiter that never resumed is closed
  107 + // here, or nothing would own it.
HITGNC   108 + 42 ~native_accept_awaitable()
  109 + {
HITGNC   110 + 42 if (peer_impl_)
HITGNC   111 + 2 discard_peer(acc_, peer_impl_);
HITGNC   112 + 42 }
  113 +
HITCBC 106   19 [[nodiscard]] capy::results<> await_resume() const noexcept 114   19 [[nodiscard]] capy::results<> await_resume() const noexcept
107   { 115   {
HITCBC 108   19 if (!this->ec_) 116   19 if (!this->ec_)
HITCBC 109 - 15 acc_.reset_peer_impl(peer_, peer_impl_); 117 + 15 acc_.reset_peer_impl(peer_, std::exchange(peer_impl_, nullptr));
HITCBC 110   19 return {this->ec_}; 118   19 return {this->ec_};
111   } 119   }
112   120  
113   std::coroutine_handle<> 121   std::coroutine_handle<>
HITCBC 114   15 dispatch(capy::continuation& cont, capy::executor_ref ex) const 122   17 dispatch(capy::continuation& cont, capy::executor_ref ex) const
115   { 123   {
HITCBC 116   45 return acc_.get_impl().accept( 124   51 return acc_.get_impl().accept(
HITCBC 117   45 cont, ex, this->token_, &this->ec_, &peer_impl_); 125   51 cont, ex, this->token_, &this->ec_, &peer_impl_);
118   } 126   }
119   }; 127   };
120   128  
121   struct native_accept_value_awaitable 129   struct native_accept_value_awaitable
122   : detail::void_op_base<native_accept_value_awaitable> 130   : detail::void_op_base<native_accept_value_awaitable>
123   { 131   {
124 - tcp_socket peer_;  
125   native_tcp_acceptor& acc_; 132   native_tcp_acceptor& acc_;
126   mutable io_object::implementation* peer_impl_ = nullptr; 133   mutable io_object::implementation* peer_impl_ = nullptr;
127   134  
HITCBC 128 - 6 explicit native_accept_value_awaitable(native_tcp_acceptor& acc) 135 + 10 explicit native_accept_value_awaitable(
  136 + native_tcp_acceptor& acc) noexcept
DCB 129 - 6 , peer_(acc.context())  
HITCBC 130   6 : acc_(acc) 137   10 : acc_(acc)
131   { 138   {
HITCBC 132   6 } 139   10 }
133   140  
  141 + // A peer accepted for an awaiter that never resumed is closed
  142 + // here, or nothing would own it.
HITGNC   143 + 20 ~native_accept_value_awaitable()
  144 + {
HITGNC   145 + 20 if (peer_impl_)
HITGNC   146 + 2 discard_peer(acc_, peer_impl_);
HITGNC   147 + 20 }
  148 +
HITCBC 134   6 [[nodiscard]] capy::results<tcp_socket> await_resume() noexcept 149   8 [[nodiscard]] capy::results<tcp_socket> await_resume() noexcept
135   { 150   {
HITCBC 136 - 6 if (!this->ec_ && peer_impl_) 151 + 8 return take_peer(acc_, this->ec_, peer_impl_);
DCB 137 - 2 acc_.reset_peer_impl(peer_, peer_impl_);  
DCB 138 - 6 return {this->ec_, std::move(peer_)};  
139   } 152   }
140   153  
141   std::coroutine_handle<> 154   std::coroutine_handle<>
HITCBC 142   2 dispatch(capy::continuation& cont, capy::executor_ref ex) const 155   4 dispatch(capy::continuation& cont, capy::executor_ref ex) const
143   { 156   {
HITCBC 144   6 return acc_.get_impl().accept( 157   12 return acc_.get_impl().accept(
HITCBC 145   6 cont, ex, this->token_, &this->ec_, &peer_impl_); 158   12 cont, ex, this->token_, &this->ec_, &peer_impl_);
146   } 159   }
147   }; 160   };
148   161  
149   public: 162   public:
150   /** Construct a native acceptor from an execution context. 163   /** Construct a native acceptor from an execution context.
151   164  
152   @param ctx The execution context that owns this acceptor. 165   @param ctx The execution context that owns this acceptor.
153   */ 166   */
HITCBC 154   35 explicit native_tcp_acceptor(capy::execution_context& ctx) 167   39 explicit native_tcp_acceptor(capy::execution_context& ctx)
HITCBC 155   35 : tcp_acceptor(handle(ctx, ctx.use_service<service_type>())) 168   39 : tcp_acceptor(handle(ctx, ctx.use_service<service_type>()))
156   { 169   {
HITCBC 157   35 } 170   39 }
158   171  
159   /** Construct a native acceptor from an executor. 172   /** Construct a native acceptor from an executor.
160   173  
161   @param ex The executor whose context owns the acceptor. 174   @param ex The executor whose context owns the acceptor.
162   175  
163   @tparam Ex A type satisfying @ref capy::Executor. Must not 176   @tparam Ex A type satisfying @ref capy::Executor. Must not
164   be `native_tcp_acceptor` itself (disables implicit 177   be `native_tcp_acceptor` itself (disables implicit
165   conversion from move). 178   conversion from move).
166   */ 179   */
167   template<class Ex> 180   template<class Ex>
168   requires(!std::same_as<std::remove_cvref_t<Ex>, native_tcp_acceptor>) && 181   requires(!std::same_as<std::remove_cvref_t<Ex>, native_tcp_acceptor>) &&
169   capy::Executor<Ex> 182   capy::Executor<Ex>
170   explicit native_tcp_acceptor(Ex const& ex) 183   explicit native_tcp_acceptor(Ex const& ex)
171   : native_tcp_acceptor(ex.context()) 184   : native_tcp_acceptor(ex.context())
172   { 185   {
173   } 186   }
174   187  
  188 + /** Construct a native acceptor that is open, bound, and listening.
  189 +
  190 + Creates a fully bound listening acceptor in a single
  191 + expression, throwing the codes the piecewise `open()` +
  192 + `set_option()` + `bind()` + `listen()` path reports. The
  193 + address family is deduced from @p ep, and address reuse is
  194 + configured as by the @ref tcp_acceptor convenience
  195 + constructor.
  196 +
  197 + @param ctx The execution context that owns this acceptor.
  198 + @param ep The local endpoint to bind to.
  199 + @param backlog The maximum pending connection queue length.
  200 +
  201 + @throws std::system_error on open, configuration, bind, or
  202 + listen failure.
  203 + */
HITGNC   204 + 8 native_tcp_acceptor(
  205 + capy::execution_context& ctx, endpoint ep, int backlog = 128)
  206 + : tcp_acceptor(
HITGNC   207 + 8 handle(ctx, ctx.use_service<service_type>()), ep, backlog)
  208 + {
HITGNC   209 + 6 }
  210 +
  211 + /** Construct a native acceptor that is open, bound, and listening.
  212 +
  213 + @param ex The executor whose context owns the acceptor.
  214 + @param ep The local endpoint to bind to.
  215 + @param backlog The maximum pending connection queue length.
  216 +
  217 + @tparam Ex A type satisfying @ref capy::Executor.
  218 +
  219 + @throws std::system_error on open, configuration, bind, or
  220 + listen failure.
  221 + */
  222 + template<class Ex>
  223 + requires capy::Executor<Ex>
HITGNC   224 + 2 native_tcp_acceptor(Ex const& ex, endpoint ep, int backlog = 128)
HITGNC   225 + 2 : native_tcp_acceptor(ex.context(), ep, backlog)
  226 + {
HITGNC   227 + 2 }
  228 +
175   /** Move construct. 229   /** Move construct.
176   230  
177   @param other The acceptor to move from. 231   @param other The acceptor to move from.
178   232  
179   @pre No awaitables returned by @p other's methods exist. 233   @pre No awaitables returned by @p other's methods exist.
180   @pre The execution context associated with @p other must 234   @pre The execution context associated with @p other must
181   outlive this acceptor. 235   outlive this acceptor.
182   */ 236   */
HITCBC 183   4 native_tcp_acceptor(native_tcp_acceptor&&) noexcept = default; 237   4 native_tcp_acceptor(native_tcp_acceptor&&) noexcept = default;
184   238  
185   /** Move assign. 239   /** Move assign.
186   240  
187   @param other The acceptor to move from. 241   @param other The acceptor to move from.
188   242  
189   @pre No awaitables returned by either `*this` or @p other's 243   @pre No awaitables returned by either `*this` or @p other's
190   methods exist. 244   methods exist.
191   @pre The execution context associated with @p other must 245   @pre The execution context associated with @p other must
192   outlive this acceptor. 246   outlive this acceptor.
193   */ 247   */
194   native_tcp_acceptor& operator=(native_tcp_acceptor&&) noexcept = default; 248   native_tcp_acceptor& operator=(native_tcp_acceptor&&) noexcept = default;
195   249  
196   /// Copy construction is disabled; the handle is uniquely owned. 250   /// Copy construction is disabled; the handle is uniquely owned.
197   native_tcp_acceptor(native_tcp_acceptor const&) = delete; 251   native_tcp_acceptor(native_tcp_acceptor const&) = delete;
198   /// Copy assignment is disabled; the handle is uniquely owned. 252   /// Copy assignment is disabled; the handle is uniquely owned.
199   native_tcp_acceptor& operator=(native_tcp_acceptor const&) = delete; 253   native_tcp_acceptor& operator=(native_tcp_acceptor const&) = delete;
200   254  
201   /** Asynchronously accept an incoming connection. 255   /** Asynchronously accept an incoming connection.
202   256  
203   Calls the backend implementation directly, bypassing virtual 257   Calls the backend implementation directly, bypassing virtual
204   dispatch. Otherwise identical to @ref tcp_acceptor::accept. 258   dispatch. Otherwise identical to @ref tcp_acceptor::accept.
205   259  
206   @param peer The socket to receive the accepted connection. 260   @param peer The socket to receive the accepted connection.
207   261  
208   @return An awaitable yielding `results<>`. 262   @return An awaitable yielding `results<>`.
209   263  
210 - A closed acceptor reports `errc::bad_file_descriptor`. 264 + A closed or moved-from acceptor reports
  265 + `errc::bad_file_descriptor`.
211   266  
212   Both this acceptor and @p peer must outlive the returned 267   Both this acceptor and @p peer must outlive the returned
213   awaitable. 268   awaitable.
214   */ 269   */
HITCBC 215   19 [[nodiscard]] auto accept(tcp_socket& peer) 270   21 [[nodiscard]] auto accept(tcp_socket& peer)
216   { 271   {
HITCBC 217   19 native_accept_awaitable aw(*this, peer); 272   21 native_accept_awaitable aw(*this, peer);
HITCBC 218   19 if (!is_open()) 273   21 if (!is_open())
HITCBC 219   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 274   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 220   19 return aw; 275   21 return aw;
221   } 276   }
222   277  
223   /** Asynchronously accept an incoming connection, returning the peer. 278   /** Asynchronously accept an incoming connection, returning the peer.
224   279  
225   Calls the backend implementation directly, bypassing virtual 280   Calls the backend implementation directly, bypassing virtual
226   dispatch. Otherwise identical to @ref tcp_acceptor::accept(). 281   dispatch. Otherwise identical to @ref tcp_acceptor::accept().
227   282  
228   @return An awaitable yielding `results<tcp_socket>`. 283   @return An awaitable yielding `results<tcp_socket>`.
229   284  
230 - A closed acceptor reports `errc::bad_file_descriptor`. 285 + A closed or moved-from acceptor reports
231 - 286 + `errc::bad_file_descriptor`.
232 - @throws std::logic_error If the acceptor is moved-from. 287 + On failure the returned socket is default-constructed and not
  288 + open. It has no context, so it may only be destroyed, assigned,
  289 + or checked with `is_open()`.
233   290  
234   This acceptor must outlive the returned awaitable. 291   This acceptor must outlive the returned awaitable.
235   */ 292   */
HITCBC 236   8 [[nodiscard]] auto accept() 293   10 [[nodiscard]] auto accept()
237 - // The awaitable builds the peer from context(), which a  
238 - // moved-from acceptor no longer has.  
239 - if (!h_)  
DCB 240 - 8 detail::throw_logic_error("accept: acceptor moved-from");  
ECB 241   2 { 294   {
HITCBC 242   6 native_accept_value_awaitable aw(*this); 295   10 native_accept_value_awaitable aw(*this);
HITCBC 243   6 if (!is_open()) 296   10 if (!is_open())
HITCBC 244   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 297   4 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 245   6 return aw; 298   10 return aw;
246   } 299   }
247   300  
248   /** Asynchronously wait for the acceptor to be ready. 301   /** Asynchronously wait for the acceptor to be ready.
249   302  
250   Calls the backend implementation directly, bypassing virtual 303   Calls the backend implementation directly, bypassing virtual
251   dispatch. Otherwise identical to @ref tcp_acceptor::wait. 304   dispatch. Otherwise identical to @ref tcp_acceptor::wait.
252   305  
253   @param w The wait direction (typically `wait_type::read`). 306   @param w The wait direction (typically `wait_type::read`).
254   307  
255   @return An awaitable yielding `results<>`. 308   @return An awaitable yielding `results<>`.
256   */ 309   */
HITCBC 257   6 [[nodiscard]] auto wait(wait_type w) 310   6 [[nodiscard]] auto wait(wait_type w)
258   { 311   {
HITCBC 259   6 return native_wait_awaitable(*this, w); 312   6 return native_wait_awaitable(*this, w);
260   } 313   }
261   }; 314   };
262   315  
263   } // namespace boost::corosio 316   } // namespace boost::corosio
264   317  
265   #endif 318   #endif