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