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