include/boost/corosio/native/detail/posix/posix_resolver.hpp

100.0% Lines (2/0/2) 100.0% List of functions (2/0/2)
posix_resolver.hpp
f(x) Functions (2)
Line TLA Hits 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_DETAIL_POSIX_POSIX_RESOLVER_HPP
11 #define BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_RESOLVER_HPP
12
13 #include <boost/corosio/detail/platform.hpp>
14
15 #if BOOST_COROSIO_POSIX
16
17 #include <boost/corosio/detail/config.hpp>
18 #include <boost/corosio/resolver.hpp>
19 #include <boost/capy/ex/execution_context.hpp>
20
21 #include <boost/corosio/native/detail/endpoint_convert.hpp>
22 #include <boost/corosio/detail/intrusive.hpp>
23 #include <boost/corosio/detail/dispatch_coro.hpp>
24 #include <boost/corosio/detail/scheduler_op.hpp>
25 #include <boost/corosio/detail/thread_pool.hpp>
26 #include <boost/corosio/native/detail/coro_op.hpp>
27
28 #include <boost/corosio/detail/scheduler.hpp>
29 #include <boost/corosio/resolver_results.hpp>
30 #include <boost/capy/ex/executor_ref.hpp>
31 #include <coroutine>
32 #include <boost/capy/error.hpp>
33
34 #include <netdb.h>
35 #include <netinet/in.h>
36 #include <sys/socket.h>
37
38 #include <atomic>
39 #include <memory>
40 #include <optional>
41 #include <stop_token>
42 #include <string>
43
44 /*
45 POSIX Resolver Service
46 ======================
47
48 POSIX getaddrinfo() is a blocking call that cannot be monitored with
49 epoll/kqueue/io_uring. Blocking calls are dispatched to a shared
50 resolver_thread_pool service which reuses threads across operations.
51
52 Cancellation
53 ------------
54 getaddrinfo() cannot be interrupted mid-call. We use an atomic flag to
55 indicate cancellation was requested. The worker thread checks this flag
56 after getaddrinfo() returns and reports the appropriate error.
57
58 Class Hierarchy
59 ---------------
60 - posix_resolver_service (execution_context service, one per context)
61 - Owns all posix_resolver instances via shared_ptr
62 - Stores scheduler* for posting completions
63 - posix_resolver (one per resolver object)
64 - Contains embedded resolve_op and reverse_resolve_op for reuse
65 - Uses shared_from_this to prevent premature destruction
66 - resolve_op (forward resolution state)
67 - Uses getaddrinfo() to resolve host/service to endpoints
68 - reverse_resolve_op (reverse resolution state)
69 - Uses getnameinfo() to resolve endpoint to host/service
70
71 Completion Flow
72 ---------------
73 Forward resolution:
74 1. resolve() sets up op_, posts work to the thread pool
75 2. Pool thread runs getaddrinfo() (blocking)
76 3. Pool thread stores results in op_.stored_results
77 4. Pool thread calls svc_.post(&op_) to queue completion
78 5. Scheduler invokes op_() which resumes the coroutine
79
80 Reverse resolution follows the same pattern using getnameinfo().
81
82 Single-Inflight Constraint
83 --------------------------
84 Each resolver has ONE embedded op_ for forward and ONE reverse_op_ for
85 reverse resolution. Concurrent operations of the same type on the same
86 resolver would corrupt state. Users must serialize operations per-resolver.
87
88 Shutdown
89 --------
90 The resolver service cancels all resolvers and clears the impl map.
91 The thread pool service shuts down separately via execution_context
92 service ordering, joining all worker threads.
93 */
94
95 namespace boost::corosio::detail {
96
97 struct scheduler;
98
99 namespace posix_resolver_detail {
100
101 // Convert resolve_flags to addrinfo ai_flags
102 int flags_to_hints(resolve_flags flags);
103
104 // Convert reverse_flags to getnameinfo NI_* flags
105 int flags_to_ni_flags(reverse_flags flags);
106
107 // Convert addrinfo results to resolver_results
108 resolver_results convert_results(
109 struct addrinfo* ai, std::string_view host, std::string_view service);
110
111 // Convert getaddrinfo error codes to std::error_code
112 std::error_code make_gai_error(int gai_err);
113
114 } // namespace posix_resolver_detail
115
116 class posix_resolver_service;
117
118 /** Resolver implementation for POSIX backends.
119
120 Each resolver instance contains a single embedded operation object (op_)
121 that is reused for each resolve() call. This design avoids per-operation
122 heap allocation but imposes a critical constraint:
123
124 @par Single-Inflight Contract
125
126 Only ONE resolve operation may be in progress at a time per resolver
127 instance. Calling resolve() while a previous resolve() is still pending
128 results in undefined behavior:
129
130 - The new call overwrites op_ fields (host, service, coroutine handle)
131 - The worker thread from the first call reads corrupted state
132 - The wrong coroutine may be resumed, or resumed multiple times
133 - Data races occur on non-atomic op_ members
134
135 @par Safe Usage Patterns
136
137 @code
138 // CORRECT: Sequential resolves
139 auto [ec1, r1] = co_await resolver.resolve("host1", "80");
140 auto [ec2, r2] = co_await resolver.resolve("host2", "80");
141
142 // CORRECT: Parallel resolves with separate resolver instances
143 resolver r1(ctx), r2(ctx);
144 auto [ec1, res1] = co_await r1.resolve("host1", "80"); // in one coroutine
145 auto [ec2, res2] = co_await r2.resolve("host2", "80"); // in another
146
147 // WRONG: Concurrent resolves on same resolver
148 // These may run concurrently if launched in parallel - UNDEFINED BEHAVIOR
149 auto f1 = resolver.resolve("host1", "80");
150 auto f2 = resolver.resolve("host2", "80"); // BAD: overlaps with f1
151 @endcode
152
153 @par Thread Safety
154 Distinct objects: Safe.
155 Shared objects: Unsafe. See single-inflight contract above.
156 */
157 class posix_resolver final
158 : public resolver::implementation
159 , public std::enable_shared_from_this<posix_resolver>
160 , public intrusive_list<posix_resolver>::node
161 {
162 friend class posix_resolver_service;
163
164 public:
165 // resolve_op - operation state for a single DNS resolution
166
167 struct resolve_op : coro_op
168 {
169 /// Where the endpoints are handed back.
170 resolver_results* out = nullptr;
171
172 // Input parameters (owned copies for thread safety)
173 std::string host;
174 std::string service;
175 resolve_flags flags = resolve_flags::none;
176
177 // Result storage (populated by worker thread)
178 resolver_results stored_results;
179 int gai_error = 0;
180
181 57x resolve_op() = default;
182
183 void reset() noexcept;
184 void operator()() override;
185 void destroy() override;
186 };
187
188 // reverse_resolve_op - operation state for reverse DNS resolution
189
190 struct reverse_resolve_op : coro_op
191 {
192 /// Where the name is handed back.
193 reverse_resolver_result* result_out = nullptr;
194
195 // Input parameters
196 endpoint ep;
197 reverse_flags flags = reverse_flags::none;
198
199 // Result storage (populated by worker thread)
200 std::string stored_host;
201 std::string stored_service;
202 int gai_error = 0;
203
204 57x reverse_resolve_op() = default;
205
206 void reset() noexcept;
207 void operator()() override;
208 void destroy() override;
209 };
210
211 /// Embedded pool work item for thread pool dispatch.
212 struct pool_op : pool_work_item
213 {
214 /// Resolver that owns this work item.
215 posix_resolver* resolver_ = nullptr;
216
217 /// Prevent impl destruction while work is in flight.
218 std::shared_ptr<posix_resolver> ref_;
219 };
220
221 explicit posix_resolver(posix_resolver_service& svc) noexcept;
222
223 std::coroutine_handle<> resolve(
224 std::coroutine_handle<>,
225 capy::executor_ref,
226 std::string_view host,
227 std::string_view service,
228 resolve_flags flags,
229 std::stop_token,
230 std::error_code*,
231 resolver_results*) override;
232
233 std::coroutine_handle<> reverse_resolve(
234 std::coroutine_handle<>,
235 capy::executor_ref,
236 endpoint const& ep,
237 reverse_flags flags,
238 std::stop_token,
239 std::error_code*,
240 reverse_resolver_result*) override;
241
242 void cancel() noexcept override;
243
244 resolve_op op_;
245 reverse_resolve_op reverse_op_;
246
247 /// Pool work item for forward resolution.
248 pool_op resolve_pool_op_;
249
250 /// Pool work item for reverse resolution.
251 pool_op reverse_pool_op_;
252
253 /// Execute blocking `getaddrinfo()` on a pool thread.
254 static void do_resolve_work(pool_work_item*) noexcept;
255
256 /// Execute blocking `getnameinfo()` on a pool thread.
257 static void do_reverse_resolve_work(pool_work_item*) noexcept;
258
259 private:
260 posix_resolver_service& svc_;
261 };
262
263 } // namespace boost::corosio::detail
264
265 #endif // BOOST_COROSIO_POSIX
266
267 #endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_RESOLVER_HPP
268