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

99.7% Lines (293 / 294) 76.6% Branches (98 / 128) 100.0% Functions (30 / 30)
posix_resolver_service.hpp
f(x) Functions (30)
Function Calls Lines Branches Blocks
boost::corosio::detail::posix_resolver_service::posix_resolver_service(boost::capy::execution_context&) :42 148x 100.0% 50.0% 100.0% boost::corosio::detail::posix_resolver_service::~posix_resolver_service() :48 222x 100.0% – 100.0% boost::corosio::detail::posix_resolver_service::destroy(boost::corosio::io_object::implementation*) :55 139x 100.0% – 100.0% boost::corosio::detail::posix_resolver_service::pool() :79 56x 100.0% – 100.0% boost::corosio::detail::posix_resolver_service::resolver_unavailable() const :87 58x 100.0% – 100.0% boost::corosio::detail::posix_resolver_service::work_started() :93 56x 100.0% – 100.0% boost::corosio::detail::posix_resolver_service::work_finished() :99 56x 100.0% – 100.0% boost::corosio::detail::posix_resolver_detail::flags_to_hints(boost::corosio::resolve_flags) :117 35x 100.0% 100.0% 100.0% boost::corosio::detail::posix_resolver_detail::flags_to_ni_flags(boost::corosio::reverse_flags) :138 13x 100.0% 100.0% 100.0% boost::corosio::detail::posix_resolver_detail::convert_results(addrinfo*) :155 25x 100.0% 66.7% 82.0% boost::corosio::detail::posix_resolver_detail::make_gai_error(int) :178 20x 100.0% 100.0% 100.0% boost::corosio::detail::posix_resolver::posix_resolver(boost::corosio::detail::posix_resolver_service&) :243 150x 100.0% – 100.0% boost::corosio::detail::posix_resolver::resolve_op::reset() :251 39x 100.0% – 100.0% boost::corosio::detail::posix_resolver::resolve_op::operator()() :265 35x 100.0% 78.6% 87.0% boost::corosio::detail::posix_resolver::resolve_op::destroy() :292 3x 100.0% – 100.0% boost::corosio::detail::posix_resolver::reverse_resolve_op::reset() :304 17x 100.0% – 100.0% boost::corosio::detail::posix_resolver::reverse_resolve_op::operator()() :318 13x 94.4% 64.3% 81.0% boost::corosio::detail::posix_resolver::reverse_resolve_op::destroy() :348 3x 100.0% – 100.0% boost::corosio::detail::posix_resolver::resolve(boost::capy::continuation&, boost::capy::executor_ref, std::__1::basic_string_view<char, std::__1::char_traits<char>>, std::__1::basic_string_view<char, std::__1::char_traits<char>>, boost::corosio::resolve_flags, std::__1::stop_token, std::__1::error_code*, std::__1::vector<boost::corosio::endpoint, std::__1::allocator<boost::corosio::endpoint>>*) :360 40x 100.0% 100.0% 100.0% boost::corosio::detail::posix_resolver::reverse_resolve(boost::capy::continuation&, boost::capy::executor_ref, boost::corosio::endpoint const&, boost::corosio::reverse_flags, std::__1::stop_token, std::__1::error_code*, boost::corosio::endpoint_name*) :412 18x 100.0% 100.0% 100.0% boost::corosio::detail::posix_resolver::cancel() :462 149x 100.0% – 100.0% boost::corosio::detail::posix_resolver::do_resolve_work(boost::corosio::detail::pool_work_item*) :469 35x 100.0% 68.2% 90.0% boost::corosio::detail::posix_resolver::do_reverse_resolve_work(boost::corosio::detail::pool_work_item*) :510 13x 100.0% 62.5% 93.0% boost::corosio::detail::posix_resolver::destroy_resolve_work(boost::corosio::detail::pool_work_item*) :561 3x 100.0% 50.0% 66.0% boost::corosio::detail::posix_resolver::destroy_reverse_resolve_work(boost::corosio::detail::pool_work_item*) :570 3x 100.0% – 100.0% boost::corosio::detail::posix_resolver_service::shutdown() :581 74x 100.0% 50.0% 75.0% boost::corosio::detail::posix_resolver_service::shutdown()::'lambda'(boost::corosio::detail::posix_resolver*)::operator()(boost::corosio::detail::posix_resolver*) const :591 3x 100.0% 100.0% 100.0% boost::corosio::detail::posix_resolver_service::construct() :604 139x 100.0% – 100.0% boost::corosio::detail::posix_resolver_service::post(boost::corosio::detail::scheduler_op*) :610 48x 100.0% – 100.0% boost::corosio::detail::posix_resolver::retire() :616 139x 100.0% 50.0% 66.0%
Line Branch TLA Hits 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_DETAIL_POSIX_POSIX_RESOLVER_SERVICE_HPP
12 #define BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_RESOLVER_SERVICE_HPP
13
14 #include <boost/corosio/detail/platform.hpp>
15
16 #if BOOST_COROSIO_POSIX
17
18 #include <boost/corosio/native/detail/posix/posix_resolver.hpp>
19 #include <boost/corosio/native/detail/reactor/reactor_scheduler.hpp>
20 #include <boost/corosio/detail/object_pool.hpp>
21 #include <boost/corosio/detail/object_ref.hpp>
22 #include <boost/corosio/detail/thread_pool.hpp>
23
24 #include <vector>
25
26 namespace boost::corosio::detail {
27
28 /** Resolver service for POSIX backends.
29
30 Owns all posix_resolver instances. Thread lifecycle is managed
31 by the thread_pool service.
32 */
33 class BOOST_COROSIO_DECL posix_resolver_service final
34 : public capy::execution_context::service
35 , public io_object::io_service
36 {
37 friend class posix_resolver;
38
39 public:
40 using key_type = posix_resolver_service;
41
42 222x explicit posix_resolver_service(capy::execution_context& ctx)
43
1/2
✓ Branch 0 taken 74 times.
✗ Branch 1 not taken.
74x : sched_(&get_scheduler(ctx))
44 74x , pool_(ctx)
45 222x {
46 148x }
47
48 222x ~posix_resolver_service() override = default;
49
50 posix_resolver_service(posix_resolver_service const&) = delete;
51 posix_resolver_service& operator=(posix_resolver_service const&) = delete;
52
53 io_object::implementation* construct() override;
54
55 139x void destroy(io_object::implementation* p) override
56 {
57 139x auto& impl = static_cast<posix_resolver&>(*p);
58 139x impl.cancel();
59 139x release(&impl);
60 139x }
61
62 void shutdown() override;
63
64 void post(scheduler_op* op);
65
66 /** Return the resolver thread pool.
67
68 The pool's service is created on first use, so this can fail
69 where a plain accessor could not. Its workers start later, on
70 the first post, and a thread the system refuses there is
71 reported by that post rather than thrown here.
72
73 @throws std::bad_alloc If the service cannot be allocated.
74
75 @return The context's shared blocking-I/O pool.
76
77 @see thread_pool_ref::get
78 */
79 56x thread_pool& pool()
80 {
81 56x return pool_.get();
82 }
83
84 /// True when the resolver thread pool is unavailable: the `unsafe` tier,
85 /// whose lockless scheduler cannot accept the pool's cross-thread
86 /// completions.
87 58x bool resolver_unavailable() const noexcept
88 {
89 58x return sched_->scheduler_locking_disabled();
90 }
91
92 /// Count an operation as work on this service's context.
93 56x void work_started() noexcept
94 {
95 56x sched_->work_started();
96 56x }
97
98 /// Release a count taken by @ref work_started.
99 56x void work_finished() noexcept
100 {
101 56x sched_->work_finished();
102 56x }
103
104 private:
105 scheduler* sched_;
106 thread_pool_ref pool_;
107 object_pool<posix_resolver> object_pool_;
108 };
109
110 // ---------------------------------------------------------------------------
111 // Inline implementation
112 // ---------------------------------------------------------------------------
113
114 // posix_resolver_detail helpers
115
116 inline int
117 35x posix_resolver_detail::flags_to_hints(resolve_flags flags)
118 {
119 35x int hints = 0;
120
121
2/2
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 1 time.
35x if ((flags & resolve_flags::passive) != resolve_flags::none)
122 1x hints |= AI_PASSIVE;
123
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 21 times.
35x if ((flags & resolve_flags::numeric_host) != resolve_flags::none)
124 21x hints |= AI_NUMERICHOST;
125
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 15 times.
35x if ((flags & resolve_flags::numeric_service) != resolve_flags::none)
126 15x hints |= AI_NUMERICSERV;
127
2/2
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 1 time.
35x if ((flags & resolve_flags::address_configured) != resolve_flags::none)
128 1x hints |= AI_ADDRCONFIG;
129
2/2
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 1 time.
35x if ((flags & resolve_flags::v4_mapped) != resolve_flags::none)
130 1x hints |= AI_V4MAPPED;
131
2/2
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 1 time.
35x if ((flags & resolve_flags::all_matching) != resolve_flags::none)
132 1x hints |= AI_ALL;
133
134 35x return hints;
135 }
136
137 inline int
138 13x posix_resolver_detail::flags_to_ni_flags(reverse_flags flags)
139 {
140 13x int ni_flags = 0;
141
142
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 6 times.
13x if ((flags & reverse_flags::numeric_host) != reverse_flags::none)
143 6x ni_flags |= NI_NUMERICHOST;
144
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 6 times.
13x if ((flags & reverse_flags::numeric_service) != reverse_flags::none)
145 6x ni_flags |= NI_NUMERICSERV;
146
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 1 time.
13x if ((flags & reverse_flags::name_required) != reverse_flags::none)
147 1x ni_flags |= NI_NAMEREQD;
148
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 1 time.
13x if ((flags & reverse_flags::datagram_service) != reverse_flags::none)
149 1x ni_flags |= NI_DGRAM;
150
151 13x return ni_flags;
152 }
153
154 inline std::vector<endpoint>
155 25x posix_resolver_detail::convert_results(struct addrinfo* ai)
156 {
157 25x std::vector<endpoint> endpoints;
158
1/2
✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
25x endpoints.reserve(4); // Most lookups return 1-4 addresses
159
160
2/2
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 25 times.
59x for (auto* p = ai; p != nullptr; p = p->ai_next)
161 {
162
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 12 times.
34x if (p->ai_family == AF_INET)
163 {
164 22x auto* addr = reinterpret_cast<sockaddr_in*>(p->ai_addr);
165
1/2
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
22x endpoints.push_back(from_sockaddr_in(*addr));
166 22x }
167
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12x else if (p->ai_family == AF_INET6)
168 {
169 12x auto* addr = reinterpret_cast<sockaddr_in6*>(p->ai_addr);
170
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12x endpoints.push_back(from_sockaddr_in6(*addr));
171 12x }
172 34x }
173
174 25x return endpoints;
175 25x }
176
177 inline std::error_code
178 20x posix_resolver_detail::make_gai_error(int gai_err)
179 {
180 // Map GAI errors to appropriate generic error codes
181
10/10
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 1 time.
✓ Branch 6 taken 7 times.
✓ Branch 7 taken 1 time.
✓ Branch 8 taken 1 time.
✓ Branch 9 taken 1 time.
20x switch (gai_err)
182 {
183 case EAI_AGAIN:
184 // Temporary failure - try again later
185 1x return std::error_code(
186 static_cast<int>(std::errc::resource_unavailable_try_again),
187 1x std::generic_category());
188
189 case EAI_BADFLAGS:
190 // Invalid flags
191 1x return std::error_code(
192 static_cast<int>(std::errc::invalid_argument),
193 1x std::generic_category());
194
195 case EAI_FAIL:
196 // Non-recoverable failure
197 5x return std::error_code(
198 5x static_cast<int>(std::errc::io_error), std::generic_category());
199
200 case EAI_FAMILY:
201 // Address family not supported
202 1x return std::error_code(
203 static_cast<int>(std::errc::address_family_not_supported),
204 1x std::generic_category());
205
206 case EAI_MEMORY:
207 // Memory allocation failure
208 1x return std::error_code(
209 static_cast<int>(std::errc::not_enough_memory),
210 1x std::generic_category());
211
212 case EAI_NONAME:
213 // Host or service not found
214 7x return std::error_code(
215 static_cast<int>(std::errc::no_such_device_or_address),
216 7x std::generic_category());
217
218 case EAI_SERVICE:
219 // Service not supported for socket type
220 1x return std::error_code(
221 static_cast<int>(std::errc::invalid_argument),
222 1x std::generic_category());
223
224 case EAI_SOCKTYPE:
225 // Socket type not supported
226 1x return std::error_code(
227 static_cast<int>(std::errc::not_supported),
228 1x std::generic_category());
229
230 case EAI_SYSTEM:
231 // System error - use errno
232 1x return std::error_code(errno, std::generic_category());
233
234 default:
235 // Unknown error
236 1x return std::error_code(
237 1x static_cast<int>(std::errc::io_error), std::generic_category());
238 }
239 20x }
240
241 // posix_resolver
242
243 300x inline posix_resolver::posix_resolver(posix_resolver_service& svc) noexcept
244 75x : svc_(svc)
245 225x {
246 150x }
247
248 // posix_resolver::resolve_op implementation
249
250 inline void
251 39x posix_resolver::resolve_op::reset() noexcept
252 {
253 39x host.clear();
254 39x service.clear();
255 39x flags = resolve_flags::none;
256 39x stored_results = std::vector<endpoint>{};
257 39x gai_error = 0;
258 39x cancelled.store(false, std::memory_order_relaxed);
259 39x stop_cb.reset();
260 39x ec_out = nullptr;
261 39x out = nullptr;
262 39x }
263
264 inline void
265 35x posix_resolver::resolve_op::operator()()
266 {
267 35x stop_cb.reset(); // Disconnect stop callback
268
269 35x bool const was_cancelled = cancelled.load(std::memory_order_acquire);
270
271
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35 times.
35x if (ec_out)
272 {
273
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 33 times.
35x if (was_cancelled)
274 2x *ec_out = capy::error::canceled;
275
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 25 times.
33x else if (gai_error != 0)
276 8x *ec_out = posix_resolver_detail::make_gai_error(gai_error);
277 else
278 25x *ec_out = {}; // Clear on success
279 35x }
280
281
5/6
✓ Branch 0 taken 35 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 33 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 8 times.
✓ Branch 5 taken 25 times.
35x if (out && !was_cancelled && gai_error == 0)
282 25x *out = std::move(stored_results);
283
284 // Hold the keepalive across the dispatch: it may be the last
285 // reference to the implementation this op is embedded in.
286 35x auto prevent_destroy = std::move(object_ref_);
287 35x svc->work_finished();
288
1/2
✓ Branch 0 taken 35 times.
✗ Branch 1 not taken.
35x dispatch_coro(ex, *cont).resume();
289 35x }
290
291 inline void
292 3x posix_resolver::resolve_op::destroy()
293 {
294 // Copied first: the abandon may unwind the frame at once, destroying
295 // the resolver, and with it this op.
296 3x auto* s = svc;
297 3x destroy_awaiting();
298 3x s->work_finished();
299 3x }
300
301 // posix_resolver::reverse_resolve_op implementation
302
303 inline void
304 17x posix_resolver::reverse_resolve_op::reset() noexcept
305 {
306 17x ep = endpoint{};
307 17x flags = reverse_flags::none;
308 17x stored_host.clear();
309 17x stored_service.clear();
310 17x gai_error = 0;
311 17x cancelled.store(false, std::memory_order_relaxed);
312 17x stop_cb.reset();
313 17x ec_out = nullptr;
314 17x result_out = nullptr;
315 17x }
316
317 inline void
318 13x posix_resolver::reverse_resolve_op::operator()()
319 {
320 13x stop_cb.reset(); // Disconnect stop callback
321
322 13x bool const was_cancelled = cancelled.load(std::memory_order_acquire);
323
324
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
13x if (ec_out)
325 {
326
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
13x if (was_cancelled)
327 ✗ *ec_out = capy::error::canceled;
328
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 10 times.
13x else if (gai_error != 0)
329 3x *ec_out = posix_resolver_detail::make_gai_error(gai_error);
330 else
331 10x *ec_out = {}; // Clear on success
332 13x }
333
334
4/6
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 13 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 10 times.
13x if (result_out && !was_cancelled && gai_error == 0)
335 {
336 10x *result_out =
337 10x endpoint_name{std::move(stored_host), std::move(stored_service)};
338 10x }
339
340 // Hold the keepalive across the dispatch: it may be the last
341 // reference to the implementation this op is embedded in.
342 13x auto prevent_destroy = std::move(object_ref_);
343 13x svc->work_finished();
344
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
13x dispatch_coro(ex, *cont).resume();
345 13x }
346
347 inline void
348 3x posix_resolver::reverse_resolve_op::destroy()
349 {
350 // Copied first: the abandon may unwind the frame at once, destroying
351 // the resolver, and with it this op.
352 3x auto* s = svc;
353 3x destroy_awaiting();
354 3x s->work_finished();
355 3x }
356
357 // posix_resolver implementation
358
359 inline std::coroutine_handle<>
360 40x posix_resolver::resolve(
361 capy::continuation& cont,
362 capy::executor_ref ex,
363 std::string_view host,
364 std::string_view service,
365 resolve_flags flags,
366 std::stop_token token,
367 std::error_code* ec,
368 std::vector<endpoint>* out)
369 {
370
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 39 times.
40x if (svc_.resolver_unavailable())
371 {
372 1x *ec = std::make_error_code(std::errc::operation_not_supported);
373 1x return dispatch_coro(ex, cont);
374 }
375
376 39x auto& op = op_;
377 39x op.reset();
378 39x op.cont = &cont;
379 39x op.ex = ex;
380 39x op.ec_out = ec;
381 39x op.out = out;
382 39x op.host = host;
383 39x op.service = service;
384 39x op.flags = flags;
385 39x op.start(token);
386
387 // Keep this context alive while resolution is pending
388 39x op.svc = &svc_;
389 39x svc_.work_started();
390
391 // Prevent impl destruction while work is in flight
392 39x resolve_pool_op_.resolver_ = this;
393 39x resolve_pool_op_.ref_ = detail::object_ref(this);
394 39x resolve_pool_op_.func_ = &posix_resolver::do_resolve_work;
395 39x resolve_pool_op_.destroy_ = &posix_resolver::destroy_resolve_work;
396
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 38 times.
39x if (auto pec = svc_.pool().post(&resolve_pool_op_))
397 {
398 // The pool is shutting down, or the system refused it a thread.
399 // Nothing of this resolve went cross-thread, so it answers here
400 // like the no-resolver exit above rather than through a
401 // completion the scheduler has to carry back.
402 1x resolve_pool_op_.ref_.reset();
403 1x op.stop_cb.reset();
404 1x svc_.work_finished();
405 1x *ec = pec;
406 1x return dispatch_coro(ex, cont);
407 }
408 38x return std::noop_coroutine();
409 40x }
410
411 inline std::coroutine_handle<>
412 18x posix_resolver::reverse_resolve(
413 capy::continuation& cont,
414 capy::executor_ref ex,
415 endpoint const& ep,
416 reverse_flags flags,
417 std::stop_token token,
418 std::error_code* ec,
419 endpoint_name* result_out)
420 {
421
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 17 times.
18x if (svc_.resolver_unavailable())
422 {
423 1x *ec = std::make_error_code(std::errc::operation_not_supported);
424 1x return dispatch_coro(ex, cont);
425 }
426
427 17x auto& op = reverse_op_;
428 17x op.reset();
429 17x op.cont = &cont;
430 17x op.ex = ex;
431 17x op.ec_out = ec;
432 17x op.result_out = result_out;
433 17x op.ep = ep;
434 17x op.flags = flags;
435 17x op.start(token);
436
437 // Keep this context alive while resolution is pending
438 17x op.svc = &svc_;
439 17x svc_.work_started();
440
441 // Prevent impl destruction while work is in flight
442 17x reverse_pool_op_.resolver_ = this;
443 17x reverse_pool_op_.ref_ = detail::object_ref(this);
444 17x reverse_pool_op_.func_ = &posix_resolver::do_reverse_resolve_work;
445 17x reverse_pool_op_.destroy_ = &posix_resolver::destroy_reverse_resolve_work;
446
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 16 times.
17x if (auto pec = svc_.pool().post(&reverse_pool_op_))
447 {
448 // The pool is shutting down, or the system refused it a thread.
449 // Nothing of this resolve went cross-thread, so it answers here
450 // like the no-resolver exit above rather than through a
451 // completion the scheduler has to carry back.
452 1x reverse_pool_op_.ref_.reset();
453 1x op.stop_cb.reset();
454 1x svc_.work_finished();
455 1x *ec = pec;
456 1x return dispatch_coro(ex, cont);
457 }
458 16x return std::noop_coroutine();
459 18x }
460
461 inline void
462 149x posix_resolver::cancel() noexcept
463 {
464 149x op_.request_cancel();
465 149x reverse_op_.request_cancel();
466 149x }
467
468 inline void
469 35x posix_resolver::do_resolve_work(pool_work_item* w) noexcept
470 {
471 35x auto* pw = static_cast<pool_op*>(w);
472 35x auto* self = pw->resolver_;
473
474 35x struct addrinfo hints{};
475 35x hints.ai_family = AF_UNSPEC;
476 35x hints.ai_socktype = SOCK_STREAM;
477
1/2
✓ Branch 0 taken 35 times.
✗ Branch 1 not taken.
35x hints.ai_flags = posix_resolver_detail::flags_to_hints(self->op_.flags);
478
479 35x struct addrinfo* ai = nullptr;
480
1/2
✓ Branch 0 taken 35 times.
✗ Branch 1 not taken.
35x int result = ::getaddrinfo(
481
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35 times.
35x self->op_.host.empty() ? nullptr : self->op_.host.c_str(),
482
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 30 times.
35x self->op_.service.empty() ? nullptr : self->op_.service.c_str(), &hints,
483 &ai);
484
485
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 33 times.
35x if (!self->op_.cancelled.load(std::memory_order_acquire))
486 {
487
3/4
✓ Branch 0 taken 25 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 25 times.
✗ Branch 3 not taken.
33x if (result == 0 && ai)
488 {
489 25x self->op_.stored_results =
490
1/2
✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
25x posix_resolver_detail::convert_results(ai);
491 25x self->op_.gai_error = 0;
492 25x }
493 else
494 {
495 8x self->op_.gai_error = result;
496 }
497 33x }
498
499
2/2
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 8 times.
35x if (ai)
500
1/2
✓ Branch 0 taken 27 times.
✗ Branch 1 not taken.
27x ::freeaddrinfo(ai);
501
502 // Hand the keepalive to the op: the completion waits in the
503 // scheduler's queue, and the implementation embedding it must
504 // outlive that wait. Nothing may touch *self after the post.
505 35x self->op_.object_ref_ = std::move(pw->ref_);
506
1/2
✓ Branch 0 taken 35 times.
✗ Branch 1 not taken.
35x self->svc_.post(&self->op_);
507 35x }
508
509 inline void
510 13x posix_resolver::do_reverse_resolve_work(pool_work_item* w) noexcept
511 {
512 13x auto* pw = static_cast<pool_op*>(w);
513 13x auto* self = pw->resolver_;
514
515 13x sockaddr_storage ss{};
516 socklen_t ss_len;
517
518
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 2 times.
13x if (self->reverse_op_.ep.is_v4())
519 {
520 11x auto sa = to_sockaddr_in(self->reverse_op_.ep);
521 11x std::memcpy(&ss, &sa, sizeof(sa));
522 11x ss_len = sizeof(sockaddr_in);
523 11x }
524 else
525 {
526 2x auto sa = to_sockaddr_in6(self->reverse_op_.ep);
527 2x std::memcpy(&ss, &sa, sizeof(sa));
528 2x ss_len = sizeof(sockaddr_in6);
529 }
530
531 char host[NI_MAXHOST];
532 char service[NI_MAXSERV];
533
534
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
13x int result = ::getnameinfo(
535 13x reinterpret_cast<sockaddr*>(&ss), ss_len, host, sizeof(host), service,
536 sizeof(service),
537
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
13x posix_resolver_detail::flags_to_ni_flags(self->reverse_op_.flags));
538
539
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
13x if (!self->reverse_op_.cancelled.load(std::memory_order_acquire))
540 {
541
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 3 times.
13x if (result == 0)
542 {
543
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10x self->reverse_op_.stored_host = host;
544
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10x self->reverse_op_.stored_service = service;
545 10x self->reverse_op_.gai_error = 0;
546 10x }
547 else
548 {
549 3x self->reverse_op_.gai_error = result;
550 }
551 13x }
552
553 // Hand the keepalive to the op: the completion waits in the
554 // scheduler's queue, and the implementation embedding it must
555 // outlive that wait. Nothing may touch *self after the post.
556 13x self->reverse_op_.object_ref_ = std::move(pw->ref_);
557
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
13x self->svc_.post(&self->reverse_op_);
558 13x }
559
560 inline void
561 3x posix_resolver::destroy_resolve_work(pool_work_item* w) noexcept
562 {
563 3x auto* pw = static_cast<pool_op*>(w);
564 3x auto* self = pw->resolver_;
565 3x self->op_.object_ref_ = std::move(pw->ref_);
566
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3x self->op_.destroy();
567 3x }
568
569 inline void
570 3x posix_resolver::destroy_reverse_resolve_work(pool_work_item* w) noexcept
571 {
572 3x auto* pw = static_cast<pool_op*>(w);
573 3x auto* self = pw->resolver_;
574 3x self->reverse_op_.object_ref_ = std::move(pw->ref_);
575 3x self->reverse_op_.destroy();
576 3x }
577
578 // posix_resolver_service implementation
579
580 inline void
581 74x posix_resolver_service::shutdown()
582 {
583 // See uring_socket_service_base::shutdown(): snapshot under an
584 // acquired reference, cancel without the pool lock held; shutdown()
585 // sets shutting-down and takes the snapshot in one critical
586 // section, so a cancel that drops the last ref deletes rather than
587 // recycles. The thread pool service shuts down separately via
588 // execution_context ordering.
589 74x std::vector<posix_resolver*> live;
590
2/4
✓ Branch 0 taken 74 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 74 times.
✗ Branch 3 not taken.
148x object_pool_.shutdown(
591 77x [&](posix_resolver* r)
592 {
593 3x acquire(r);
594 3x live.push_back(r);
595 3x });
596
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 74 times.
77x for (auto* r : live)
597 {
598 3x r->cancel();
599 3x release(r);
600 }
601 74x }
602
603 inline io_object::implementation*
604 139x posix_resolver_service::construct()
605 {
606 139x return object_pool_.acquire(*this);
607 }
608
609 inline void
610 48x posix_resolver_service::post(scheduler_op* op)
611 {
612 48x sched_->post(op);
613 48x }
614
615 inline void
616 139x posix_resolver::retire() noexcept
617 {
618
1/2
✓ Branch 0 taken 139 times.
✗ Branch 1 not taken.
139x svc_.object_pool_.recycle(this);
619 139x }
620
621 } // namespace boost::corosio::detail
622
623 #endif // BOOST_COROSIO_POSIX
624
625 #endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_RESOLVER_SERVICE_HPP
626