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