90.48% Lines (57/63) 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   10 impl_type& get_impl() noexcept 67   12 impl_type& get_impl() noexcept
68   { 68   {
HITCBC 69   10 return *static_cast<impl_type*>(h_.get()); 69   12 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   2 native_wait_awaitable(native_tcp_acceptor& acc, wait_type w) noexcept 79   2 native_wait_awaitable(native_tcp_acceptor& acc, wait_type w) noexcept
HITCBC 80   2 : acc_(acc) 80   2 : acc_(acc)
HITCBC 81   2 , w_(w) 81   2 , w_(w)
82   { 82   {
HITCBC 83   2 } 83   2 }
84   84  
HITCBC 85   2 bool await_ready() const noexcept 85   2 bool await_ready() const noexcept
86   { 86   {
HITCBC 87   2 return token_.stop_requested(); 87   2 return token_.stop_requested();
88   } 88   }
89   89  
HITCBC 90   2 capy::io_result<> await_resume() const noexcept 90   2 capy::io_result<> await_resume() const noexcept
91   { 91   {
HITCBC 92   2 if (token_.stop_requested()) 92   2 if (token_.stop_requested())
MISUBC 93   return {make_error_code(std::errc::operation_canceled)}; 93   return {make_error_code(std::errc::operation_canceled)};
HITCBC 94   2 return {ec_}; 94   2 return {ec_};
95   } 95   }
96   96  
HITCBC 97   2 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 97   2 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
98   -> std::coroutine_handle<> 98   -> std::coroutine_handle<>
99   { 99   {
HITCBC 100   2 token_ = env->stop_token; 100   2 token_ = env->stop_token;
HITCBC 101   6 return acc_.get_impl().wait( 101   6 return acc_.get_impl().wait(
HITCBC 102   6 h, env->executor, w_, token_, &ec_); 102   6 h, env->executor, w_, token_, &ec_);
103   } 103   }
104   }; 104   };
105   105  
106   struct native_accept_awaitable 106   struct native_accept_awaitable
107   { 107   {
108   native_tcp_acceptor& acc_; 108   native_tcp_acceptor& acc_;
109   tcp_socket& peer_; 109   tcp_socket& peer_;
110   std::stop_token token_; 110   std::stop_token token_;
111   mutable std::error_code ec_; 111   mutable std::error_code ec_;
112   mutable io_object::implementation* peer_impl_ = nullptr; 112   mutable io_object::implementation* peer_impl_ = nullptr;
113   113  
HITCBC 114   8 native_accept_awaitable( 114   8 native_accept_awaitable(
115   native_tcp_acceptor& acc, tcp_socket& peer) noexcept 115   native_tcp_acceptor& acc, tcp_socket& peer) noexcept
HITCBC 116   8 : acc_(acc) 116   8 : acc_(acc)
HITCBC 117   8 , peer_(peer) 117   8 , peer_(peer)
118   { 118   {
HITCBC 119   8 } 119   8 }
120   120  
HITCBC 121   8 bool await_ready() const noexcept 121   8 bool await_ready() const noexcept
122   { 122   {
HITCBC 123   8 return token_.stop_requested(); 123   8 return token_.stop_requested();
124   } 124   }
125   125  
HITCBC 126   8 capy::io_result<> await_resume() const noexcept 126   8 capy::io_result<> await_resume() const noexcept
127   { 127   {
HITCBC 128   8 if (token_.stop_requested()) 128   8 if (token_.stop_requested())
MISUBC 129   return {make_error_code(std::errc::operation_canceled)}; 129   return {make_error_code(std::errc::operation_canceled)};
HITCBC 130   8 if (!ec_) 130   8 if (!ec_)
HITCBC 131   8 acc_.reset_peer_impl(peer_, peer_impl_); 131   8 acc_.reset_peer_impl(peer_, peer_impl_);
HITCBC 132   8 return {ec_}; 132   8 return {ec_};
133   } 133   }
134   134  
HITCBC 135   8 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 135   8 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
136   -> std::coroutine_handle<> 136   -> std::coroutine_handle<>
137   { 137   {
HITCBC 138   8 token_ = env->stop_token; 138   8 token_ = env->stop_token;
HITCBC 139   24 return acc_.get_impl().accept( 139   24 return acc_.get_impl().accept(
HITCBC 140   24 h, env->executor, token_, &ec_, &peer_impl_); 140   24 h, env->executor, token_, &ec_, &peer_impl_);
141   } 141   }
142   }; 142   };
143   143  
  144 + struct native_accept_value_awaitable
  145 + {
  146 + native_tcp_acceptor& acc_;
  147 + tcp_socket peer_;
  148 + std::stop_token token_;
  149 + mutable std::error_code ec_;
  150 + mutable io_object::implementation* peer_impl_ = nullptr;
  151 +
HITGNC   152 + 2 explicit native_accept_value_awaitable(native_tcp_acceptor& acc)
HITGNC   153 + 2 : acc_(acc)
HITGNC   154 + 2 , peer_(acc.context())
  155 + {
HITGNC   156 + 2 }
  157 +
HITGNC   158 + 2 bool await_ready() const noexcept
  159 + {
HITGNC   160 + 2 return token_.stop_requested();
  161 + }
  162 +
HITGNC   163 + 2 capy::io_result<tcp_socket> await_resume() noexcept
  164 + {
HITGNC   165 + 2 if (token_.stop_requested())
MISUNC   166 + return {make_error_code(std::errc::operation_canceled),
MISUNC   167 + std::move(peer_)};
HITGNC   168 + 2 if (!ec_ && peer_impl_)
HITGNC   169 + 2 acc_.reset_peer_impl(peer_, peer_impl_);
HITGNC   170 + 2 return {ec_, std::move(peer_)};
  171 + }
  172 +
HITGNC   173 + 2 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
  174 + -> std::coroutine_handle<>
  175 + {
HITGNC   176 + 2 token_ = env->stop_token;
HITGNC   177 + 6 return acc_.get_impl().accept(
HITGNC   178 + 6 h, env->executor, token_, &ec_, &peer_impl_);
  179 + }
  180 + };
  181 +
144   public: 182   public:
145   /** Construct a native acceptor from an execution context. 183   /** Construct a native acceptor from an execution context.
146   184  
147   @param ctx The execution context that will own this acceptor. 185   @param ctx The execution context that will own this acceptor.
148   */ 186   */
HITCBC 149   18 explicit native_tcp_acceptor(capy::execution_context& ctx) 187   20 explicit native_tcp_acceptor(capy::execution_context& ctx)
HITCBC 150   18 : tcp_acceptor(create_handle<service_type>(ctx)) 188   20 : tcp_acceptor(create_handle<service_type>(ctx))
151   { 189   {
HITCBC 152   18 } 190   20 }
153   191  
154   /** Construct a native acceptor from an executor. 192   /** Construct a native acceptor from an executor.
155   193  
156   @param ex The executor whose context will own the acceptor. 194   @param ex The executor whose context will own the acceptor.
157   */ 195   */
158   template<class Ex> 196   template<class Ex>
159   requires(!std::same_as<std::remove_cvref_t<Ex>, native_tcp_acceptor>) && 197   requires(!std::same_as<std::remove_cvref_t<Ex>, native_tcp_acceptor>) &&
160   capy::Executor<Ex> 198   capy::Executor<Ex>
161   explicit native_tcp_acceptor(Ex const& ex) 199   explicit native_tcp_acceptor(Ex const& ex)
162   : native_tcp_acceptor(ex.context()) 200   : native_tcp_acceptor(ex.context())
163   { 201   {
164   } 202   }
165   203  
166   /** Move construct. 204   /** Move construct.
167   205  
168   @param other The acceptor to move from. 206   @param other The acceptor to move from.
169   207  
170   @pre No awaitables returned by @p other's methods exist. 208   @pre No awaitables returned by @p other's methods exist.
171   @pre The execution context associated with @p other must 209   @pre The execution context associated with @p other must
172   outlive this acceptor. 210   outlive this acceptor.
173   */ 211   */
HITCBC 174   2 native_tcp_acceptor(native_tcp_acceptor&&) noexcept = default; 212   2 native_tcp_acceptor(native_tcp_acceptor&&) noexcept = default;
175   213  
176   /** Move assign. 214   /** Move assign.
177   215  
178   @param other The acceptor to move from. 216   @param other The acceptor to move from.
179   217  
180   @pre No awaitables returned by either `*this` or @p other's 218   @pre No awaitables returned by either `*this` or @p other's
181   methods exist. 219   methods exist.
182   @pre The execution context associated with @p other must 220   @pre The execution context associated with @p other must
183   outlive this acceptor. 221   outlive this acceptor.
184   */ 222   */
185   native_tcp_acceptor& operator=(native_tcp_acceptor&&) noexcept = default; 223   native_tcp_acceptor& operator=(native_tcp_acceptor&&) noexcept = default;
186   224  
187   native_tcp_acceptor(native_tcp_acceptor const&) = delete; 225   native_tcp_acceptor(native_tcp_acceptor const&) = delete;
188   native_tcp_acceptor& operator=(native_tcp_acceptor const&) = delete; 226   native_tcp_acceptor& operator=(native_tcp_acceptor const&) = delete;
189   227  
190   /** Asynchronously accept an incoming connection. 228   /** Asynchronously accept an incoming connection.
191   229  
192   Calls the backend implementation directly, bypassing virtual 230   Calls the backend implementation directly, bypassing virtual
193   dispatch. Otherwise identical to @ref tcp_acceptor::accept. 231   dispatch. Otherwise identical to @ref tcp_acceptor::accept.
194   232  
195   @param peer The socket to receive the accepted connection. 233   @param peer The socket to receive the accepted connection.
196   234  
197   @return An awaitable yielding `io_result<>`. 235   @return An awaitable yielding `io_result<>`.
198   236  
199   @throws std::logic_error if the acceptor is not listening. 237   @throws std::logic_error if the acceptor is not listening.
200   238  
201   Both this acceptor and @p peer must outlive the returned 239   Both this acceptor and @p peer must outlive the returned
202   awaitable. 240   awaitable.
203   */ 241   */
HITCBC 204   8 auto accept(tcp_socket& peer) 242   8 auto accept(tcp_socket& peer)
205   { 243   {
HITCBC 206   8 if (!is_open()) 244   8 if (!is_open())
MISUBC 207   detail::throw_logic_error("accept: acceptor not listening"); 245   detail::throw_logic_error("accept: acceptor not listening");
HITCBC 208   8 return native_accept_awaitable(*this, peer); 246   8 return native_accept_awaitable(*this, peer);
  247 + }
  248 +
  249 + /** Asynchronously accept an incoming connection, returning the peer.
  250 +
  251 + Calls the backend implementation directly, bypassing virtual
  252 + dispatch. Otherwise identical to @ref tcp_acceptor::accept().
  253 +
  254 + @return An awaitable yielding `io_result<tcp_socket>`.
  255 +
  256 + @throws std::logic_error if the acceptor is not listening.
  257 +
  258 + This acceptor must outlive the returned awaitable.
  259 + */
HITGNC   260 + 2 auto accept()
  261 + {
HITGNC   262 + 2 if (!is_open())
MISUNC   263 + detail::throw_logic_error("accept: acceptor not listening");
HITGNC   264 + 2 return native_accept_value_awaitable(*this);
209   } 265   }
210   266  
211   /** Asynchronously wait for the acceptor to be ready. 267   /** Asynchronously wait for the acceptor to be ready.
212   268  
213   Calls the backend implementation directly, bypassing virtual 269   Calls the backend implementation directly, bypassing virtual
214   dispatch. Otherwise identical to @ref tcp_acceptor::wait. 270   dispatch. Otherwise identical to @ref tcp_acceptor::wait.
215   271  
216   @param w The wait direction (typically `wait_type::read`). 272   @param w The wait direction (typically `wait_type::read`).
217   273  
218   @return An awaitable yielding `io_result<>`. 274   @return An awaitable yielding `io_result<>`.
219   */ 275   */
HITCBC 220   2 [[nodiscard]] auto wait(wait_type w) 276   2 [[nodiscard]] auto wait(wait_type w)
221   { 277   {
HITCBC 222   2 return native_wait_awaitable(*this, w); 278   2 return native_wait_awaitable(*this, w);
223   } 279   }
224   }; 280   };
225   281  
226   } // namespace boost::corosio 282   } // namespace boost::corosio
227   283  
228   #endif 284   #endif