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   25 impl_type& get_impl() noexcept 69   25 impl_type& get_impl() noexcept
70   { 70   {
HITCBC 71   25 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   21 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   21 : acc_(acc) 101   21 : acc_(acc)
HITCBC 102   21 , peer_(peer) 102   21 , peer_(peer)
103   { 103   {
HITCBC 104   21 } 104   21 }
105   105  
106   // A peer accepted for an awaiter that never resumed is closed 106   // A peer accepted for an awaiter that never resumed is closed
107   // here, or nothing would own it. 107   // here, or nothing would own it.
HITCBC 108   42 ~native_accept_awaitable() 108   42 ~native_accept_awaitable()
109   { 109   {
HITCBC 110   42 if (peer_impl_) 110   42 if (peer_impl_)
HITCBC 111   2 discard_peer(acc_, peer_impl_); 111   2 discard_peer(acc_, peer_impl_);
HITCBC 112   42 } 112   42 }
113   113  
HITCBC 114   19 [[nodiscard]] capy::results<> await_resume() const noexcept 114   19 [[nodiscard]] capy::results<> await_resume() const noexcept
115   { 115   {
HITCBC 116   19 if (!this->ec_) 116   19 if (!this->ec_)
HITCBC 117   15 acc_.reset_peer_impl(peer_, std::exchange(peer_impl_, nullptr)); 117   15 acc_.reset_peer_impl(peer_, std::exchange(peer_impl_, nullptr));
HITCBC 118   19 return {this->ec_}; 118   19 return {this->ec_};
119   } 119   }
120   120  
121   std::coroutine_handle<> 121   std::coroutine_handle<>
HITCBC 122   17 dispatch(capy::continuation& cont, capy::executor_ref ex) const 122   17 dispatch(capy::continuation& cont, capy::executor_ref ex) const
123   { 123   {
HITCBC 124   51 return acc_.get_impl().accept( 124   51 return acc_.get_impl().accept(
HITCBC 125   51 cont, ex, this->token_, &this->ec_, &peer_impl_); 125   51 cont, ex, this->token_, &this->ec_, &peer_impl_);
126   } 126   }
127   }; 127   };
128   128  
129   struct native_accept_value_awaitable 129   struct native_accept_value_awaitable
130   : detail::void_op_base<native_accept_value_awaitable> 130   : detail::void_op_base<native_accept_value_awaitable>
131   { 131   {
132   native_tcp_acceptor& acc_; 132   native_tcp_acceptor& acc_;
133   mutable io_object::implementation* peer_impl_ = nullptr; 133   mutable io_object::implementation* peer_impl_ = nullptr;
134   134  
HITCBC 135   10 explicit native_accept_value_awaitable( 135   10 explicit native_accept_value_awaitable(
136   native_tcp_acceptor& acc) noexcept 136   native_tcp_acceptor& acc) noexcept
HITCBC 137   10 : acc_(acc) 137   10 : acc_(acc)
138   { 138   {
HITCBC 139   10 } 139   10 }
140   140  
141   // A peer accepted for an awaiter that never resumed is closed 141   // A peer accepted for an awaiter that never resumed is closed
142   // here, or nothing would own it. 142   // here, or nothing would own it.
HITCBC 143   20 ~native_accept_value_awaitable() 143   20 ~native_accept_value_awaitable()
144   { 144   {
HITCBC 145   20 if (peer_impl_) 145   20 if (peer_impl_)
HITCBC 146   2 discard_peer(acc_, peer_impl_); 146   2 discard_peer(acc_, peer_impl_);
HITCBC 147   20 } 147   20 }
148   148  
HITCBC 149   8 [[nodiscard]] capy::results<tcp_socket> await_resume() noexcept 149   8 [[nodiscard]] capy::results<tcp_socket> await_resume() noexcept
150   { 150   {
HITCBC 151   8 return take_peer(acc_, this->ec_, peer_impl_); 151   8 return take_peer(acc_, this->ec_, peer_impl_);
152   } 152   }
153   153  
154   std::coroutine_handle<> 154   std::coroutine_handle<>
HITCBC 155   4 dispatch(capy::continuation& cont, capy::executor_ref ex) const 155   4 dispatch(capy::continuation& cont, capy::executor_ref ex) const
156   { 156   {
HITCBC 157   12 return acc_.get_impl().accept( 157   12 return acc_.get_impl().accept(
HITCBC 158   12 cont, ex, this->token_, &this->ec_, &peer_impl_); 158   12 cont, ex, this->token_, &this->ec_, &peer_impl_);
159   } 159   }
160   }; 160   };
161   161  
162   public: 162   public:
163   /** Construct a native acceptor from an execution context. 163   /** Construct a native acceptor from an execution context.
164   164  
165   @param ctx The execution context that owns this acceptor. 165   @param ctx The execution context that owns this acceptor.
166   */ 166   */
HITCBC 167   39 explicit native_tcp_acceptor(capy::execution_context& ctx) 167   39 explicit native_tcp_acceptor(capy::execution_context& ctx)
HITCBC 168   39 : tcp_acceptor(handle(ctx, ctx.use_service<service_type>())) 168   39 : tcp_acceptor(handle(ctx, ctx.use_service<service_type>()))
169   { 169   {
HITCBC 170   39 } 170   39 }
171   171  
172   /** Construct a native acceptor from an executor. 172   /** Construct a native acceptor from an executor.
173   173  
174   @param ex The executor whose context owns the acceptor. 174   @param ex The executor whose context owns the acceptor.
175   175  
176   @tparam Ex A type satisfying @ref capy::Executor. Must not 176   @tparam Ex A type satisfying @ref capy::Executor. Must not
177   be `native_tcp_acceptor` itself (disables implicit 177   be `native_tcp_acceptor` itself (disables implicit
178   conversion from move). 178   conversion from move).
179   */ 179   */
180   template<class Ex> 180   template<class Ex>
181   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>) &&
182   capy::Executor<Ex> 182   capy::Executor<Ex>
183   explicit native_tcp_acceptor(Ex const& ex) 183   explicit native_tcp_acceptor(Ex const& ex)
184   : native_tcp_acceptor(ex.context()) 184   : native_tcp_acceptor(ex.context())
185   { 185   {
186   } 186   }
187   187  
188   /** Construct a native acceptor that is open, bound, and listening. 188   /** Construct a native acceptor that is open, bound, and listening.
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, and address reuse is 193   address family is deduced from @p ep, and address reuse is
194   configured as by the @ref tcp_acceptor convenience 194   configured as by the @ref tcp_acceptor convenience
195   constructor. 195   constructor.
196   196  
197   @param ctx The execution context that owns this acceptor. 197   @param ctx The execution context that owns this acceptor.
198   @param ep The local endpoint to bind to. 198   @param ep The local endpoint to bind to.
199   @param backlog The maximum pending connection queue length. 199   @param backlog The maximum pending connection queue length.
200   200  
201   @throws std::system_error on open, configuration, bind, or 201   @throws std::system_error on open, configuration, bind, or
202   listen failure. 202   listen failure.
203   */ 203   */
HITCBC 204   8 native_tcp_acceptor( 204   8 native_tcp_acceptor(
205   capy::execution_context& ctx, endpoint ep, int backlog = 128) 205   capy::execution_context& ctx, endpoint ep, int backlog = 128)
206   : tcp_acceptor( 206   : tcp_acceptor(
HITCBC 207   8 handle(ctx, ctx.use_service<service_type>()), ep, backlog) 207   8 handle(ctx, ctx.use_service<service_type>()), ep, backlog)
208   { 208   {
HITCBC 209   6 } 209   6 }
210   210  
211   /** Construct a native acceptor that is open, bound, and listening. 211   /** Construct a native acceptor that is open, bound, and listening.
212   212  
213   @param ex The executor whose context owns the acceptor. 213   @param ex The executor whose context owns the acceptor.
214   @param ep The local endpoint to bind to. 214   @param ep The local endpoint to bind to.
215   @param backlog The maximum pending connection queue length. 215   @param backlog The maximum pending connection queue length.
216   216  
217   @tparam Ex A type satisfying @ref capy::Executor. 217   @tparam Ex A type satisfying @ref capy::Executor.
218   218  
219   @throws std::system_error on open, configuration, bind, or 219   @throws std::system_error on open, configuration, bind, or
220   listen failure. 220   listen failure.
221   */ 221   */
222   template<class Ex> 222   template<class Ex>
223   requires capy::Executor<Ex> 223   requires capy::Executor<Ex>
HITCBC 224   2 native_tcp_acceptor(Ex const& ex, endpoint ep, int backlog = 128) 224   2 native_tcp_acceptor(Ex const& ex, endpoint ep, int backlog = 128)
HITCBC 225   2 : native_tcp_acceptor(ex.context(), ep, backlog) 225   2 : native_tcp_acceptor(ex.context(), ep, backlog)
226   { 226   {
HITCBC 227   2 } 227   2 }
228   228  
229   /** Move construct. 229   /** Move construct.
230   230  
231   @param other The acceptor to move from. 231   @param other The acceptor to move from.
232   232  
233   @pre No awaitables returned by @p other's methods exist. 233   @pre No awaitables returned by @p other's methods exist.
234   @pre The execution context associated with @p other must 234   @pre The execution context associated with @p other must
235   outlive this acceptor. 235   outlive this acceptor.
236   */ 236   */
HITCBC 237   4 native_tcp_acceptor(native_tcp_acceptor&&) noexcept = default; 237   4 native_tcp_acceptor(native_tcp_acceptor&&) noexcept = default;
238   238  
239   /** Move assign. 239   /** Move assign.
240   240  
241   @param other The acceptor to move from. 241   @param other The acceptor to move from.
242   242  
243   @pre No awaitables returned by either `*this` or @p other's 243   @pre No awaitables returned by either `*this` or @p other's
244   methods exist. 244   methods exist.
245   @pre The execution context associated with @p other must 245   @pre The execution context associated with @p other must
246   outlive this acceptor. 246   outlive this acceptor.
247   */ 247   */
248   native_tcp_acceptor& operator=(native_tcp_acceptor&&) noexcept = default; 248   native_tcp_acceptor& operator=(native_tcp_acceptor&&) noexcept = default;
249   249  
250   /// Copy construction is disabled; the handle is uniquely owned. 250   /// Copy construction is disabled; the handle is uniquely owned.
251   native_tcp_acceptor(native_tcp_acceptor const&) = delete; 251   native_tcp_acceptor(native_tcp_acceptor const&) = delete;
252   /// Copy assignment is disabled; the handle is uniquely owned. 252   /// Copy assignment is disabled; the handle is uniquely owned.
253   native_tcp_acceptor& operator=(native_tcp_acceptor const&) = delete; 253   native_tcp_acceptor& operator=(native_tcp_acceptor const&) = delete;
254   254  
255   /** Asynchronously accept an incoming connection. 255   /** Asynchronously accept an incoming connection.
256   256  
257   Calls the backend implementation directly, bypassing virtual 257   Calls the backend implementation directly, bypassing virtual
258   dispatch. Otherwise identical to @ref tcp_acceptor::accept. 258   dispatch. Otherwise identical to @ref tcp_acceptor::accept.
259   259  
260   @param peer The socket to receive the accepted connection. 260   @param peer The socket to receive the accepted connection.
261   261  
262   @return An awaitable yielding `results<>`. 262   @return An awaitable yielding `results<>`.
263   263  
264   A closed or moved-from acceptor reports 264   A closed or moved-from acceptor reports
265   `errc::bad_file_descriptor`. 265   `errc::bad_file_descriptor`.
266   266  
267   Both this acceptor and @p peer must outlive the returned 267   Both this acceptor and @p peer must outlive the returned
268   awaitable. 268   awaitable.
269   */ 269   */
HITCBC 270   21 [[nodiscard]] auto accept(tcp_socket& peer) 270   21 [[nodiscard]] auto accept(tcp_socket& peer)
271   { 271   {
HITCBC 272   21 native_accept_awaitable aw(*this, peer); 272   21 native_accept_awaitable aw(*this, peer);
HITCBC 273   21 if (!is_open()) 273   21 if (!is_open())
HITCBC 274   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 274   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 275   21 return aw; 275   21 return aw;
276   } 276   }
277   277  
278   /** Asynchronously accept an incoming connection, returning the peer. 278   /** Asynchronously accept an incoming connection, returning the peer.
279   279  
280   Calls the backend implementation directly, bypassing virtual 280   Calls the backend implementation directly, bypassing virtual
281   dispatch. Otherwise identical to @ref tcp_acceptor::accept(). 281   dispatch. Otherwise identical to @ref tcp_acceptor::accept().
282   282  
283   @return An awaitable yielding `results<tcp_socket>`. 283   @return An awaitable yielding `results<tcp_socket>`.
284   284  
285   A closed or moved-from acceptor reports 285   A closed or moved-from acceptor reports
286   `errc::bad_file_descriptor`. 286   `errc::bad_file_descriptor`.
287   On failure the returned socket is default-constructed and not 287   On failure the returned socket is default-constructed and not
288   open. It has no context, so it may only be destroyed, assigned, 288   open. It has no context, so it may only be destroyed, assigned,
289   or checked with `is_open()`. 289   or checked with `is_open()`.
290   290  
291   This acceptor must outlive the returned awaitable. 291   This acceptor must outlive the returned awaitable.
292   */ 292   */
HITCBC 293   10 [[nodiscard]] auto accept() 293   10 [[nodiscard]] auto accept()
294   { 294   {
HITCBC 295   10 native_accept_value_awaitable aw(*this); 295   10 native_accept_value_awaitable aw(*this);
HITCBC 296   10 if (!is_open()) 296   10 if (!is_open())
HITCBC 297   4 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 297   4 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 298   10 return aw; 298   10 return aw;
299   } 299   }
300   300  
301   /** Asynchronously wait for the acceptor to be ready. 301   /** Asynchronously wait for the acceptor to be ready.
302   302  
303   Calls the backend implementation directly, bypassing virtual 303   Calls the backend implementation directly, bypassing virtual
304   dispatch. Otherwise identical to @ref tcp_acceptor::wait. 304   dispatch. Otherwise identical to @ref tcp_acceptor::wait.
305   305  
306   @param w The wait direction (typically `wait_type::read`). 306   @param w The wait direction (typically `wait_type::read`).
307   307  
308   @return An awaitable yielding `results<>`. 308   @return An awaitable yielding `results<>`.
309   */ 309   */
HITCBC 310   6 [[nodiscard]] auto wait(wait_type w) 310   6 [[nodiscard]] auto wait(wait_type w)
311   { 311   {
HITCBC 312   6 return native_wait_awaitable(*this, w); 312   6 return native_wait_awaitable(*this, w);
313   } 313   }
314   }; 314   };
315   315  
316   } // namespace boost::corosio 316   } // namespace boost::corosio
317   317  
318   #endif 318   #endif