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