94.03% Lines (63/67) 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   // 3   //
4   // Distributed under the Boost Software License, Version 1.0. (See accompanying 4   // Distributed under the Boost Software License, Version 1.0. (See accompanying
5   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6   // 6   //
7   // Official repository: https://github.com/cppalliance/corosio 7   // Official repository: https://github.com/cppalliance/corosio
8   // 8   //
9   9  
10   #ifndef BOOST_COROSIO_NATIVE_NATIVE_TCP_ACCEPTOR_HPP 10   #ifndef BOOST_COROSIO_NATIVE_NATIVE_TCP_ACCEPTOR_HPP
11   #define BOOST_COROSIO_NATIVE_NATIVE_TCP_ACCEPTOR_HPP 11   #define BOOST_COROSIO_NATIVE_NATIVE_TCP_ACCEPTOR_HPP
12   12  
13   #include <boost/corosio/tcp_acceptor.hpp> 13   #include <boost/corosio/tcp_acceptor.hpp>
14   #include <boost/corosio/backend.hpp> 14   #include <boost/corosio/backend.hpp>
15   15  
16   #ifndef BOOST_COROSIO_MRDOCS 16   #ifndef BOOST_COROSIO_MRDOCS
17   #if BOOST_COROSIO_HAS_EPOLL 17   #if BOOST_COROSIO_HAS_EPOLL
18   #include <boost/corosio/native/detail/epoll/epoll_types.hpp> 18   #include <boost/corosio/native/detail/epoll/epoll_types.hpp>
19   #endif 19   #endif
20   20  
21   #if BOOST_COROSIO_HAS_SELECT 21   #if BOOST_COROSIO_HAS_SELECT
22   #include <boost/corosio/native/detail/select/select_types.hpp> 22   #include <boost/corosio/native/detail/select/select_types.hpp>
23   #endif 23   #endif
24   24  
25   #if BOOST_COROSIO_HAS_KQUEUE 25   #if BOOST_COROSIO_HAS_KQUEUE
26   #include <boost/corosio/native/detail/kqueue/kqueue_types.hpp> 26   #include <boost/corosio/native/detail/kqueue/kqueue_types.hpp>
27   #endif 27   #endif
28   28  
29   #if BOOST_COROSIO_HAS_IOCP 29   #if BOOST_COROSIO_HAS_IOCP
30   #include <boost/corosio/native/detail/iocp/win_tcp_acceptor_service.hpp> 30   #include <boost/corosio/native/detail/iocp/win_tcp_acceptor_service.hpp>
31   #endif 31   #endif
32   32  
33   #if BOOST_COROSIO_HAS_IO_URING 33   #if BOOST_COROSIO_HAS_IO_URING
34   #include <boost/corosio/native/detail/io_uring/io_uring_types.hpp> 34   #include <boost/corosio/native/detail/io_uring/io_uring_types.hpp>
35   #endif 35   #endif
36   #endif // !BOOST_COROSIO_MRDOCS 36   #endif // !BOOST_COROSIO_MRDOCS
37   37  
38   namespace boost::corosio { 38   namespace boost::corosio {
39   39  
40   /** An asynchronous TCP acceptor with devirtualized accept operations. 40   /** An asynchronous TCP acceptor with devirtualized accept operations.
41   41  
42   This class template inherits from @ref tcp_acceptor and shadows 42   This class template inherits from @ref tcp_acceptor and shadows
43   the `accept` operation with a version that calls the backend 43   the `accept` operation with a version that calls the backend
44   implementation directly, allowing the compiler to inline through 44   implementation directly, allowing the compiler to inline through
45   the entire call chain. 45   the entire call chain.
46   46  
47   Non-async operations (`listen`, `close`, `cancel`) remain 47   Non-async operations (`listen`, `close`, `cancel`) remain
48   unchanged and dispatch through the compiled library. 48   unchanged and dispatch through the compiled library.
49   49  
50   A `native_tcp_acceptor` IS-A `tcp_acceptor` and can be passed 50   A `native_tcp_acceptor` IS-A `tcp_acceptor` and can be passed
51   to any function expecting `tcp_acceptor&`. 51   to any function expecting `tcp_acceptor&`.
52   52  
53   @tparam Backend A backend tag value (e.g., `epoll`). 53   @tparam Backend A backend tag value (e.g., `epoll`).
54   54  
55   @par Thread Safety 55   @par Thread Safety
56   Same as @ref tcp_acceptor. 56   Same as @ref tcp_acceptor.
57   57  
58   @see tcp_acceptor, epoll_t, iocp_t 58   @see tcp_acceptor, epoll_t, iocp_t
59   */ 59   */
60   template<auto Backend> 60   template<auto Backend>
61   class native_tcp_acceptor : public tcp_acceptor 61   class native_tcp_acceptor : public tcp_acceptor
62   { 62   {
63   using backend_type = decltype(Backend); 63   using backend_type = decltype(Backend);
64   using impl_type = typename backend_type::tcp_acceptor_type; 64   using impl_type = typename backend_type::tcp_acceptor_type;
65   using service_type = typename backend_type::tcp_acceptor_service_type; 65   using service_type = typename backend_type::tcp_acceptor_service_type;
66   66  
HITCBC 67   15 impl_type& get_impl() noexcept 67   15 impl_type& get_impl() noexcept
68   { 68   {
HITCBC 69   15 return *static_cast<impl_type*>(h_.get()); 69   15 return *static_cast<impl_type*>(h_.get());
70   } 70   }
71   71  
72   struct native_wait_awaitable 72   struct native_wait_awaitable
73   { 73   {
74   native_tcp_acceptor& acc_; 74   native_tcp_acceptor& acc_;
75   wait_type w_; 75   wait_type w_;
76   std::stop_token token_; 76   std::stop_token token_;
77   mutable std::error_code ec_; 77   mutable std::error_code ec_;
78   78  
HITCBC 79   4 native_wait_awaitable(native_tcp_acceptor& acc, wait_type w) noexcept 79   4 native_wait_awaitable(native_tcp_acceptor& acc, wait_type w) noexcept
HITCBC 80   4 : acc_(acc) 80   4 : acc_(acc)
HITCBC 81   4 , w_(w) 81   4 , w_(w)
82   { 82   {
HITCBC 83   4 } 83   4 }
84   84  
HITCBC 85   4 bool await_ready() const noexcept 85   4 bool await_ready() const noexcept
86   { 86   {
87   // A pre-set ec_ means the initiator failed before 87   // A pre-set ec_ means the initiator failed before
88   // dispatch (e.g. a closed object). 88   // dispatch (e.g. a closed object).
HITCBC 89   4 return static_cast<bool>(ec_) || token_.stop_requested(); 89   4 return static_cast<bool>(ec_) || token_.stop_requested();
90   } 90   }
91   91  
HITCBC 92   4 [[nodiscard]] capy::io_result<> await_resume() const noexcept 92   4 [[nodiscard]] capy::io_result<> await_resume() const noexcept
93   { 93   {
HITCBC 94   4 if (token_.stop_requested()) 94   4 if (token_.stop_requested())
MISUBC 95   return {make_error_code(std::errc::operation_canceled)}; 95   return {make_error_code(std::errc::operation_canceled)};
HITCBC 96   4 return {ec_}; 96   4 return {ec_};
97   } 97   }
98   98  
HITCBC 99   4 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 99   4 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
100   -> std::coroutine_handle<> 100   -> std::coroutine_handle<>
101   { 101   {
HITCBC 102   4 token_ = env->stop_token; 102   4 token_ = env->stop_token;
HITCBC 103   12 return acc_.get_impl().wait( 103   12 return acc_.get_impl().wait(
HITCBC 104   12 h, env->executor, w_, token_, &ec_); 104   12 h, env->executor, w_, token_, &ec_);
105   } 105   }
106   }; 106   };
107   107  
108   struct native_accept_awaitable 108   struct native_accept_awaitable
109   { 109   {
110   native_tcp_acceptor& acc_; 110   native_tcp_acceptor& acc_;
111   tcp_socket& peer_; 111   tcp_socket& peer_;
112   std::stop_token token_; 112   std::stop_token token_;
113   mutable std::error_code ec_; 113   mutable std::error_code ec_;
114   mutable io_object::implementation* peer_impl_ = nullptr; 114   mutable io_object::implementation* peer_impl_ = nullptr;
115   115  
HITCBC 116   11 native_accept_awaitable( 116   11 native_accept_awaitable(
117   native_tcp_acceptor& acc, tcp_socket& peer) noexcept 117   native_tcp_acceptor& acc, tcp_socket& peer) noexcept
HITCBC 118   11 : acc_(acc) 118   11 : acc_(acc)
HITCBC 119   11 , peer_(peer) 119   11 , peer_(peer)
120   { 120   {
HITCBC 121   11 } 121   11 }
122   122  
HITCBC 123   11 bool await_ready() const noexcept 123   11 bool await_ready() const noexcept
124   { 124   {
125   // A pre-set ec_ means the initiator failed before 125   // A pre-set ec_ means the initiator failed before
126   // dispatch (e.g. a closed object). 126   // dispatch (e.g. a closed object).
HITCBC 127   11 return static_cast<bool>(ec_) || token_.stop_requested(); 127   11 return static_cast<bool>(ec_) || token_.stop_requested();
128   } 128   }
129   129  
HITCBC 130   11 [[nodiscard]] capy::io_result<> await_resume() const noexcept 130   11 [[nodiscard]] capy::io_result<> await_resume() const noexcept
131   { 131   {
HITCBC 132   11 if (token_.stop_requested()) 132   11 if (token_.stop_requested())
MISUBC 133   return {make_error_code(std::errc::operation_canceled)}; 133   return {make_error_code(std::errc::operation_canceled)};
HITCBC 134   11 if (!ec_) 134   11 if (!ec_)
HITCBC 135   9 acc_.reset_peer_impl(peer_, peer_impl_); 135   9 acc_.reset_peer_impl(peer_, peer_impl_);
HITCBC 136   11 return {ec_}; 136   11 return {ec_};
137   } 137   }
138   138  
HITCBC 139   9 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 139   9 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
140   -> std::coroutine_handle<> 140   -> std::coroutine_handle<>
141   { 141   {
HITCBC 142   9 token_ = env->stop_token; 142   9 token_ = env->stop_token;
HITCBC 143   27 return acc_.get_impl().accept( 143   27 return acc_.get_impl().accept(
HITCBC 144   27 h, env->executor, token_, &ec_, &peer_impl_); 144   27 h, env->executor, token_, &ec_, &peer_impl_);
145   } 145   }
146   }; 146   };
147   147  
148   struct native_accept_value_awaitable 148   struct native_accept_value_awaitable
149   { 149   {
150   native_tcp_acceptor& acc_; 150   native_tcp_acceptor& acc_;
151   tcp_socket peer_; 151   tcp_socket peer_;
152   std::stop_token token_; 152   std::stop_token token_;
153   mutable std::error_code ec_; 153   mutable std::error_code ec_;
154   mutable io_object::implementation* peer_impl_ = nullptr; 154   mutable io_object::implementation* peer_impl_ = nullptr;
155   155  
HITCBC 156   4 explicit native_accept_value_awaitable(native_tcp_acceptor& acc) 156   4 explicit native_accept_value_awaitable(native_tcp_acceptor& acc)
HITCBC 157   4 : acc_(acc) 157   4 : acc_(acc)
HITCBC 158   4 , peer_(acc.context()) 158   4 , peer_(acc.context())
159   { 159   {
HITCBC 160   4 } 160   4 }
161   161  
HITCBC 162   4 bool await_ready() const noexcept 162   4 bool await_ready() const noexcept
163   { 163   {
164   // A pre-set ec_ means the initiator failed before 164   // A pre-set ec_ means the initiator failed before
165   // dispatch (e.g. a closed object). 165   // dispatch (e.g. a closed object).
HITCBC 166   4 return static_cast<bool>(ec_) || token_.stop_requested(); 166   4 return static_cast<bool>(ec_) || token_.stop_requested();
167   } 167   }
168   168  
HITCBC 169   4 [[nodiscard]] capy::io_result<tcp_socket> await_resume() noexcept 169   4 [[nodiscard]] capy::io_result<tcp_socket> await_resume() noexcept
170   { 170   {
HITCBC 171   4 if (token_.stop_requested()) 171   4 if (token_.stop_requested())
MISUBC 172   return {make_error_code(std::errc::operation_canceled), 172   return {make_error_code(std::errc::operation_canceled),
MISUBC 173   std::move(peer_)}; 173   std::move(peer_)};
HITCBC 174   4 if (!ec_ && peer_impl_) 174   4 if (!ec_ && peer_impl_)
HITCBC 175   2 acc_.reset_peer_impl(peer_, peer_impl_); 175   2 acc_.reset_peer_impl(peer_, peer_impl_);
HITCBC 176   4 return {ec_, std::move(peer_)}; 176   4 return {ec_, std::move(peer_)};
177   } 177   }
178   178  
HITCBC 179   2 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 179   2 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
180   -> std::coroutine_handle<> 180   -> std::coroutine_handle<>
181   { 181   {
HITCBC 182   2 token_ = env->stop_token; 182   2 token_ = env->stop_token;
HITCBC 183   6 return acc_.get_impl().accept( 183   6 return acc_.get_impl().accept(
HITCBC 184   6 h, env->executor, token_, &ec_, &peer_impl_); 184   6 h, env->executor, token_, &ec_, &peer_impl_);
185   } 185   }
186   }; 186   };
187   187  
188   public: 188   public:
189   /** Construct a native acceptor from an execution context. 189   /** Construct a native acceptor from an execution context.
190   190  
191   @param ctx The execution context that will own this acceptor. 191   @param ctx The execution context that will own this acceptor.
192   */ 192   */
HITCBC 193   27 explicit native_tcp_acceptor(capy::execution_context& ctx) 193   27 explicit native_tcp_acceptor(capy::execution_context& ctx)
HITCBC 194   27 : tcp_acceptor(create_handle<service_type>(ctx)) 194   27 : tcp_acceptor(create_handle<service_type>(ctx))
195   { 195   {
HITCBC 196   27 } 196   27 }
197   197  
198   /** Construct a native acceptor from an executor. 198   /** Construct a native acceptor from an executor.
199   199  
200   @param ex The executor whose context will own the acceptor. 200   @param ex The executor whose context will own the acceptor.
201   */ 201   */
202   template<class Ex> 202   template<class Ex>
203   requires(!std::same_as<std::remove_cvref_t<Ex>, native_tcp_acceptor>) && 203   requires(!std::same_as<std::remove_cvref_t<Ex>, native_tcp_acceptor>) &&
204   capy::Executor<Ex> 204   capy::Executor<Ex>
205   explicit native_tcp_acceptor(Ex const& ex) 205   explicit native_tcp_acceptor(Ex const& ex)
206   : native_tcp_acceptor(ex.context()) 206   : native_tcp_acceptor(ex.context())
207   { 207   {
208   } 208   }
209   209  
210   /** Move construct. 210   /** Move construct.
211   211  
212   @param other The acceptor to move from. 212   @param other The acceptor to move from.
213   213  
214   @pre No awaitables returned by @p other's methods exist. 214   @pre No awaitables returned by @p other's methods exist.
215   @pre The execution context associated with @p other must 215   @pre The execution context associated with @p other must
216   outlive this acceptor. 216   outlive this acceptor.
217   */ 217   */
HITCBC 218   4 native_tcp_acceptor(native_tcp_acceptor&&) noexcept = default; 218   4 native_tcp_acceptor(native_tcp_acceptor&&) noexcept = default;
219   219  
220   /** Move assign. 220   /** Move assign.
221   221  
222   @param other The acceptor to move from. 222   @param other The acceptor to move from.
223   223  
224   @pre No awaitables returned by either `*this` or @p other's 224   @pre No awaitables returned by either `*this` or @p other's
225   methods exist. 225   methods exist.
226   @pre The execution context associated with @p other must 226   @pre The execution context associated with @p other must
227   outlive this acceptor. 227   outlive this acceptor.
228   */ 228   */
229   native_tcp_acceptor& operator=(native_tcp_acceptor&&) noexcept = default; 229   native_tcp_acceptor& operator=(native_tcp_acceptor&&) noexcept = default;
230   230  
231   native_tcp_acceptor(native_tcp_acceptor const&) = delete; 231   native_tcp_acceptor(native_tcp_acceptor const&) = delete;
232   native_tcp_acceptor& operator=(native_tcp_acceptor const&) = delete; 232   native_tcp_acceptor& operator=(native_tcp_acceptor const&) = delete;
233   233  
234   /** Asynchronously accept an incoming connection. 234   /** Asynchronously accept an incoming connection.
235   235  
236   Calls the backend implementation directly, bypassing virtual 236   Calls the backend implementation directly, bypassing virtual
237   dispatch. Otherwise identical to @ref tcp_acceptor::accept. 237   dispatch. Otherwise identical to @ref tcp_acceptor::accept.
238   238  
239   @param peer The socket to receive the accepted connection. 239   @param peer The socket to receive the accepted connection.
240   240  
241   @return An awaitable yielding `io_result<>`. 241   @return An awaitable yielding `io_result<>`.
242   242  
243   A closed acceptor reports `errc::bad_file_descriptor`. 243   A closed acceptor reports `errc::bad_file_descriptor`.
244   244  
245   Both this acceptor and @p peer must outlive the returned 245   Both this acceptor and @p peer must outlive the returned
246   awaitable. 246   awaitable.
247   */ 247   */
HITCBC 248   11 [[nodiscard]] auto accept(tcp_socket& peer) 248   11 [[nodiscard]] auto accept(tcp_socket& peer)
249   { 249   {
HITCBC 250   11 native_accept_awaitable aw(*this, peer); 250   11 native_accept_awaitable aw(*this, peer);
HITCBC 251   11 if (!is_open()) 251   11 if (!is_open())
HITCBC 252   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 252   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 253   11 return aw; 253   11 return aw;
254   } 254   }
255   255  
256   /** Asynchronously accept an incoming connection, returning the peer. 256   /** Asynchronously accept an incoming connection, returning the peer.
257   257  
258   Calls the backend implementation directly, bypassing virtual 258   Calls the backend implementation directly, bypassing virtual
259   dispatch. Otherwise identical to @ref tcp_acceptor::accept(). 259   dispatch. Otherwise identical to @ref tcp_acceptor::accept().
260   260  
261   @return An awaitable yielding `io_result<tcp_socket>`. 261   @return An awaitable yielding `io_result<tcp_socket>`.
262   262  
263   A closed acceptor reports `errc::bad_file_descriptor`. 263   A closed acceptor reports `errc::bad_file_descriptor`.
264   264  
265   @throws std::logic_error If the acceptor has been moved from. 265   @throws std::logic_error If the acceptor has been moved from.
266   266  
267   This acceptor must outlive the returned awaitable. 267   This acceptor must outlive the returned awaitable.
268   */ 268   */
HITCBC 269   6 [[nodiscard]] auto accept() 269   6 [[nodiscard]] auto accept()
270   { 270   {
271   // The awaitable builds the peer from context(), which a 271   // The awaitable builds the peer from context(), which a
272   // moved-from acceptor no longer has. 272   // moved-from acceptor no longer has.
HITCBC 273   6 if (!h_) 273   6 if (!h_)
HITCBC 274   2 detail::throw_logic_error("accept: acceptor moved-from"); 274   2 detail::throw_logic_error("accept: acceptor moved-from");
HITCBC 275   4 native_accept_value_awaitable aw(*this); 275   4 native_accept_value_awaitable aw(*this);
HITCBC 276   4 if (!is_open()) 276   4 if (!is_open())
HITCBC 277   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 277   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 278   4 return aw; 278   4 return aw;
279   } 279   }
280   280  
281   /** Asynchronously wait for the acceptor to be ready. 281   /** Asynchronously wait for the acceptor to be ready.
282   282  
283   Calls the backend implementation directly, bypassing virtual 283   Calls the backend implementation directly, bypassing virtual
284   dispatch. Otherwise identical to @ref tcp_acceptor::wait. 284   dispatch. Otherwise identical to @ref tcp_acceptor::wait.
285   285  
286   @param w The wait direction (typically `wait_type::read`). 286   @param w The wait direction (typically `wait_type::read`).
287   287  
288   @return An awaitable yielding `io_result<>`. 288   @return An awaitable yielding `io_result<>`.
289   */ 289   */
HITCBC 290   4 [[nodiscard]] auto wait(wait_type w) 290   4 [[nodiscard]] auto wait(wait_type w)
291   { 291   {
HITCBC 292   4 return native_wait_awaitable(*this, w); 292   4 return native_wait_awaitable(*this, w);
293   } 293   }
294   }; 294   };
295   295  
296   } // namespace boost::corosio 296   } // namespace boost::corosio
297   297  
298   #endif 298   #endif