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