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 : /** An asynchronous TCP acceptor with devirtualized accept operations.
42 :
43 : This class template inherits from @ref tcp_acceptor and shadows
44 : the `accept` operation with a version that calls the backend
45 : implementation directly, allowing the compiler to inline through
46 : the 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 will own this acceptor.
152 : */
153 35 : explicit native_tcp_acceptor(capy::execution_context& ctx)
154 35 : : tcp_acceptor(create_handle<service_type>(ctx))
155 : {
156 35 : }
157 :
158 : /** Construct a native acceptor from an executor.
159 :
160 : @param ex The executor whose context will own the acceptor.
161 : */
162 : template<class Ex>
163 : requires(!std::same_as<std::remove_cvref_t<Ex>, native_tcp_acceptor>) &&
164 : capy::Executor<Ex>
165 : explicit native_tcp_acceptor(Ex const& ex)
166 : : native_tcp_acceptor(ex.context())
167 : {
168 : }
169 :
170 : /** Move construct.
171 :
172 : @param other The acceptor to move from.
173 :
174 : @pre No awaitables returned by @p other's methods exist.
175 : @pre The execution context associated with @p other must
176 : outlive this acceptor.
177 : */
178 4 : native_tcp_acceptor(native_tcp_acceptor&&) noexcept = default;
179 :
180 : /** Move assign.
181 :
182 : @param other The acceptor to move from.
183 :
184 : @pre No awaitables returned by either `*this` or @p other's
185 : methods exist.
186 : @pre The execution context associated with @p other must
187 : outlive this acceptor.
188 : */
189 : native_tcp_acceptor& operator=(native_tcp_acceptor&&) noexcept = default;
190 :
191 : native_tcp_acceptor(native_tcp_acceptor const&) = delete;
192 : native_tcp_acceptor& operator=(native_tcp_acceptor const&) = delete;
193 :
194 : /** Asynchronously accept an incoming connection.
195 :
196 : Calls the backend implementation directly, bypassing virtual
197 : dispatch. Otherwise identical to @ref tcp_acceptor::accept.
198 :
199 : @param peer The socket to receive the accepted connection.
200 :
201 : @return An awaitable yielding `io_result<>`.
202 :
203 : A closed acceptor reports `errc::bad_file_descriptor`.
204 :
205 : Both this acceptor and @p peer must outlive the returned
206 : awaitable.
207 : */
208 19 : [[nodiscard]] auto accept(tcp_socket& peer)
209 : {
210 19 : native_accept_awaitable aw(*this, peer);
211 19 : if (!is_open())
212 2 : aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
213 19 : return aw;
214 : }
215 :
216 : /** Asynchronously accept an incoming connection, returning the peer.
217 :
218 : Calls the backend implementation directly, bypassing virtual
219 : dispatch. Otherwise identical to @ref tcp_acceptor::accept().
220 :
221 : @return An awaitable yielding `io_result<tcp_socket>`.
222 :
223 : A closed acceptor reports `errc::bad_file_descriptor`.
224 :
225 : @throws std::logic_error If the acceptor has been moved from.
226 :
227 : This acceptor must outlive the returned awaitable.
228 : */
229 8 : [[nodiscard]] auto accept()
230 : {
231 : // The awaitable builds the peer from context(), which a
232 : // moved-from acceptor no longer has.
233 8 : if (!h_)
234 2 : detail::throw_logic_error("accept: acceptor moved-from");
235 6 : native_accept_value_awaitable aw(*this);
236 6 : if (!is_open())
237 2 : aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
238 6 : return aw;
239 : }
240 :
241 : /** Asynchronously wait for the acceptor to be ready.
242 :
243 : Calls the backend implementation directly, bypassing virtual
244 : dispatch. Otherwise identical to @ref tcp_acceptor::wait.
245 :
246 : @param w The wait direction (typically `wait_type::read`).
247 :
248 : @return An awaitable yielding `io_result<>`.
249 : */
250 6 : [[nodiscard]] auto wait(wait_type w)
251 : {
252 6 : return native_wait_awaitable(*this, w);
253 : }
254 : };
255 :
256 : } // namespace boost::corosio
257 :
258 : #endif
|