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

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