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 4 : impl_type& get_impl() noexcept
57 : {
58 4 : 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 4 : native_resolve_awaitable(
72 : native_resolver& self,
73 : std::string_view host,
74 : std::string_view service,
75 : resolve_flags flags) noexcept
76 4 : : self_(self)
77 8 : , host_(host)
78 8 : , service_(service)
79 4 : , flags_(flags)
80 : {
81 4 : }
82 :
83 4 : bool await_ready() const noexcept
84 : {
85 4 : return static_cast<bool>(ec_) || token_.stop_requested();
86 : }
87 :
88 : [[nodiscard]] capy::io_result<resolver_results>
89 4 : await_resume() const noexcept
90 : {
91 4 : if (token_.stop_requested())
92 2 : return {make_error_code(std::errc::operation_canceled), {}};
93 2 : return {ec_, std::move(results_)};
94 : }
95 :
96 4 : auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
97 : -> std::coroutine_handle<>
98 : {
99 4 : token_ = env->stop_token;
100 12 : return self_.get_impl().resolve(
101 4 : h, env->executor, host_, service_, flags_, token_, &ec_,
102 8 : &results_);
103 : }
104 : };
105 :
106 : struct native_reverse_awaitable
107 : {
108 : native_resolver& self_;
109 : endpoint ep_;
110 : reverse_flags flags_;
111 : std::stop_token token_;
112 : mutable std::error_code ec_;
113 : mutable reverse_resolver_result result_;
114 :
115 : native_reverse_awaitable(
116 : native_resolver& self,
117 : endpoint const& ep,
118 : reverse_flags flags) noexcept
119 : : self_(self)
120 : , ep_(ep)
121 : , flags_(flags)
122 : {
123 : }
124 :
125 : bool await_ready() const noexcept
126 : {
127 : return static_cast<bool>(ec_) || token_.stop_requested();
128 : }
129 :
130 : [[nodiscard]] capy::io_result<reverse_resolver_result>
131 : await_resume() const noexcept
132 : {
133 : if (token_.stop_requested())
134 : return {make_error_code(std::errc::operation_canceled), {}};
135 : return {ec_, std::move(result_)};
136 : }
137 :
138 : auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
139 : -> std::coroutine_handle<>
140 : {
141 : token_ = env->stop_token;
142 : return self_.get_impl().reverse_resolve(
143 : h, env->executor, ep_, flags_, token_, &ec_, &result_);
144 : }
145 : };
146 :
147 : public:
148 : /** Construct a native resolver from an execution context.
149 :
150 : @param ctx The execution context that will own this resolver.
151 : */
152 8 : explicit native_resolver(capy::execution_context& ctx) : resolver(ctx) {}
153 :
154 : /** Construct a native resolver from an executor.
155 :
156 : @param ex The executor whose context will own the resolver.
157 : */
158 : template<class Ex>
159 : requires(!std::same_as<std::remove_cvref_t<Ex>, native_resolver>) &&
160 : capy::Executor<Ex>
161 : explicit native_resolver(Ex const& ex) : native_resolver(ex.context())
162 : {
163 : }
164 :
165 : /** Move construct.
166 :
167 : @pre No awaitables returned by @p other's `resolve` methods
168 : exist.
169 : @pre The execution context associated with @p other must
170 : outlive this resolver.
171 : */
172 : native_resolver(native_resolver&&) noexcept = default;
173 :
174 : /** Move assign.
175 :
176 : @pre No awaitables returned by either `*this` or the source's
177 : `resolve` methods exist.
178 : @pre The execution context associated with the source must
179 : outlive this resolver.
180 : */
181 : native_resolver& operator=(native_resolver&&) noexcept = default;
182 :
183 : native_resolver(native_resolver const&) = delete;
184 : native_resolver& operator=(native_resolver const&) = delete;
185 :
186 : /** Asynchronously resolve a host and service to endpoints.
187 :
188 : Calls the backend implementation directly, bypassing virtual
189 : dispatch. Otherwise identical to @ref resolver::resolve.
190 :
191 : This resolver must outlive the returned awaitable.
192 :
193 : @param host The host name or address string.
194 : @param service The service name or port string.
195 :
196 : @return An awaitable yielding `io_result<resolver_results>`.
197 :
198 : @note `resolver_results` is an alias for `std::vector<resolver_entry>`;
199 : copying it deep-copies every entry. See @ref resolver::resolve.
200 : */
201 4 : [[nodiscard]] auto resolve(std::string_view host, std::string_view service)
202 : {
203 : return native_resolve_awaitable(
204 4 : *this, host, service, resolve_flags::none);
205 : }
206 :
207 : /** Asynchronously resolve a host and service with flags.
208 :
209 : This resolver must outlive the returned awaitable.
210 :
211 : @param host The host name or address string.
212 : @param service The service name or port string.
213 : @param flags Flags controlling resolution behavior.
214 :
215 : @return An awaitable yielding `io_result<resolver_results>`.
216 : */
217 : [[nodiscard]] auto resolve(
218 : std::string_view host, std::string_view service, resolve_flags flags)
219 : {
220 : return native_resolve_awaitable(*this, host, service, flags);
221 : }
222 :
223 : /** Asynchronously reverse-resolve an endpoint.
224 :
225 : Calls the backend implementation directly, bypassing virtual
226 : dispatch. Otherwise identical to the endpoint overload of
227 : @ref resolver::resolve.
228 :
229 : This resolver must outlive the returned awaitable.
230 :
231 : @param ep The endpoint to resolve.
232 :
233 : @return An awaitable yielding
234 : `io_result<reverse_resolver_result>`.
235 : */
236 : [[nodiscard]] auto resolve(endpoint const& ep)
237 : {
238 : return native_reverse_awaitable(*this, ep, reverse_flags::none);
239 : }
240 :
241 : /** Asynchronously reverse-resolve an endpoint with flags.
242 :
243 : This resolver must outlive the returned awaitable.
244 :
245 : @param ep The endpoint to resolve.
246 : @param flags Flags controlling resolution behavior.
247 :
248 : @return An awaitable yielding
249 : `io_result<reverse_resolver_result>`.
250 : */
251 : [[nodiscard]] auto resolve(endpoint const& ep, reverse_flags flags)
252 : {
253 : return native_reverse_awaitable(*this, ep, flags);
254 : }
255 : };
256 :
257 : } // namespace boost::corosio
258 :
259 : #endif
|