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_RESOLVER_HPP
12 : #define BOOST_COROSIO_NATIVE_NATIVE_RESOLVER_HPP
13 :
14 : #include <boost/corosio/resolver.hpp>
15 : #include <boost/corosio/backend.hpp>
16 :
17 : #ifndef BOOST_COROSIO_MRDOCS
18 : #if BOOST_COROSIO_HAS_EPOLL || BOOST_COROSIO_HAS_SELECT || \
19 : BOOST_COROSIO_HAS_KQUEUE
20 : #include <boost/corosio/native/detail/posix/posix_resolver_service.hpp>
21 : #endif
22 :
23 : #if BOOST_COROSIO_HAS_IOCP
24 : #include <boost/corosio/native/detail/iocp/win_resolver_service.hpp>
25 : #endif
26 : #endif // !BOOST_COROSIO_MRDOCS
27 :
28 : namespace boost::corosio {
29 :
30 : /** An asynchronous DNS resolver with devirtualized operations.
31 :
32 : This class template inherits from @ref resolver and shadows
33 : the `resolve` operations with versions that call the backend
34 : implementation directly, allowing the compiler to inline
35 : through the entire call chain.
36 :
37 : Non-async operations (`cancel`) remain unchanged and dispatch
38 : through the compiled library.
39 :
40 : A `native_resolver` IS-A `resolver` and can be passed to any
41 : function expecting `resolver&`.
42 :
43 : @tparam Backend A backend tag value (e.g., `epoll`).
44 :
45 : @par Thread Safety
46 : Same as @ref resolver.
47 :
48 : @see resolver, epoll_t, iocp_t
49 : */
50 : template<auto Backend>
51 : class native_resolver : public resolver
52 : {
53 : using backend_type = decltype(Backend);
54 : using impl_type = typename backend_type::resolver_type;
55 :
56 HIT 2 : impl_type& get_impl() noexcept
57 : {
58 2 : return *static_cast<impl_type*>(h_.get());
59 : }
60 :
61 : struct native_resolve_awaitable
62 : {
63 : native_resolver& self_;
64 : std::string host_;
65 : std::string service_;
66 : resolve_flags flags_;
67 : std::stop_token token_;
68 : mutable std::error_code ec_;
69 : mutable resolver_results results_;
70 :
71 2 : native_resolve_awaitable(
72 : native_resolver& self,
73 : std::string_view host,
74 : std::string_view service,
75 : resolve_flags flags) noexcept
76 2 : : self_(self)
77 4 : , host_(host)
78 4 : , service_(service)
79 2 : , flags_(flags)
80 : {
81 2 : }
82 :
83 2 : bool await_ready() const noexcept
84 : {
85 2 : return token_.stop_requested();
86 : }
87 :
88 2 : capy::io_result<resolver_results> await_resume() const noexcept
89 : {
90 2 : if (token_.stop_requested())
91 MIS 0 : return {make_error_code(std::errc::operation_canceled), {}};
92 HIT 2 : return {ec_, std::move(results_)};
93 : }
94 :
95 2 : auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
96 : -> std::coroutine_handle<>
97 : {
98 2 : token_ = env->stop_token;
99 6 : return self_.get_impl().resolve(
100 2 : h, env->executor, host_, service_, flags_, token_, &ec_,
101 4 : &results_);
102 : }
103 : };
104 :
105 : struct native_reverse_awaitable
106 : {
107 : native_resolver& self_;
108 : endpoint ep_;
109 : reverse_flags flags_;
110 : std::stop_token token_;
111 : mutable std::error_code ec_;
112 : mutable reverse_resolver_result result_;
113 :
114 : native_reverse_awaitable(
115 : native_resolver& self,
116 : endpoint const& ep,
117 : reverse_flags flags) noexcept
118 : : self_(self)
119 : , ep_(ep)
120 : , flags_(flags)
121 : {
122 : }
123 :
124 : bool await_ready() const noexcept
125 : {
126 : return token_.stop_requested();
127 : }
128 :
129 : capy::io_result<reverse_resolver_result> await_resume() const noexcept
130 : {
131 : if (token_.stop_requested())
132 : return {make_error_code(std::errc::operation_canceled), {}};
133 : return {ec_, std::move(result_)};
134 : }
135 :
136 : auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
137 : -> std::coroutine_handle<>
138 : {
139 : token_ = env->stop_token;
140 : return self_.get_impl().reverse_resolve(
141 : h, env->executor, ep_, flags_, token_, &ec_, &result_);
142 : }
143 : };
144 :
145 : public:
146 : /** Construct a native resolver from an execution context.
147 :
148 : @param ctx The execution context that will own this resolver.
149 : */
150 6 : explicit native_resolver(capy::execution_context& ctx) : resolver(ctx) {}
151 :
152 : /** Construct a native resolver from an executor.
153 :
154 : @param ex The executor whose context will own the resolver.
155 : */
156 : template<class Ex>
157 : requires(!std::same_as<std::remove_cvref_t<Ex>, native_resolver>) &&
158 : capy::Executor<Ex>
159 : explicit native_resolver(Ex const& ex) : native_resolver(ex.context())
160 : {
161 : }
162 :
163 : /** Move construct.
164 :
165 : @pre No awaitables returned by @p other's `resolve` methods
166 : exist.
167 : @pre The execution context associated with @p other must
168 : outlive this resolver.
169 : */
170 : native_resolver(native_resolver&&) noexcept = default;
171 :
172 : /** Move assign.
173 :
174 : @pre No awaitables returned by either `*this` or the source's
175 : `resolve` methods exist.
176 : @pre The execution context associated with the source must
177 : outlive this resolver.
178 : */
179 : native_resolver& operator=(native_resolver&&) noexcept = default;
180 :
181 : native_resolver(native_resolver const&) = delete;
182 : native_resolver& operator=(native_resolver const&) = delete;
183 :
184 : /** Asynchronously resolve a host and service to endpoints.
185 :
186 : Calls the backend implementation directly, bypassing virtual
187 : dispatch. Otherwise identical to @ref resolver::resolve.
188 :
189 : This resolver must outlive the returned awaitable.
190 :
191 : @param host The host name or address string.
192 : @param service The service name or port string.
193 :
194 : @return An awaitable yielding `io_result<resolver_results>`.
195 :
196 : @note `resolver_results` is an alias for `std::vector<resolver_entry>`;
197 : copying it deep-copies every entry. See @ref resolver::resolve.
198 : */
199 2 : auto resolve(std::string_view host, std::string_view service)
200 : {
201 : return native_resolve_awaitable(
202 2 : *this, host, service, resolve_flags::none);
203 : }
204 :
205 : /** Asynchronously resolve a host and service with flags.
206 :
207 : This resolver must outlive the returned awaitable.
208 :
209 : @param host The host name or address string.
210 : @param service The service name or port string.
211 : @param flags Flags controlling resolution behavior.
212 :
213 : @return An awaitable yielding `io_result<resolver_results>`.
214 : */
215 : auto resolve(
216 : std::string_view host, std::string_view service, resolve_flags flags)
217 : {
218 : return native_resolve_awaitable(*this, host, service, flags);
219 : }
220 :
221 : /** Asynchronously reverse-resolve an endpoint.
222 :
223 : Calls the backend implementation directly, bypassing virtual
224 : dispatch. Otherwise identical to the endpoint overload of
225 : @ref resolver::resolve.
226 :
227 : This resolver must outlive the returned awaitable.
228 :
229 : @param ep The endpoint to resolve.
230 :
231 : @return An awaitable yielding
232 : `io_result<reverse_resolver_result>`.
233 : */
234 : auto resolve(endpoint const& ep)
235 : {
236 : return native_reverse_awaitable(*this, ep, reverse_flags::none);
237 : }
238 :
239 : /** Asynchronously reverse-resolve an endpoint with flags.
240 :
241 : This resolver must outlive the returned awaitable.
242 :
243 : @param ep The endpoint to resolve.
244 : @param flags Flags controlling resolution behavior.
245 :
246 : @return An awaitable yielding
247 : `io_result<reverse_resolver_result>`.
248 : */
249 : auto resolve(endpoint const& ep, reverse_flags flags)
250 : {
251 : return native_reverse_awaitable(*this, ep, flags);
252 : }
253 : };
254 :
255 : } // namespace boost::corosio
256 :
257 : #endif
|