include/boost/corosio/native/detail/iocp/win_resolver_service.hpp

92.1% Lines (221/240) 100.0% List of functions (25/25) 83.5% Branches (101/121)
win_resolver_service.hpp
f(x) Functions (25)
Function Calls Lines Branches Blocks
boost::corosio::detail::win_resolver_service::destroy(boost::corosio::io_object::implementation*) :49 39x 100.0% 100.0% boost::corosio::detail::win_resolver_service::pool() :85 13x 100.0% 100.0% boost::corosio::detail::resolver_detail::to_wide[abi:cxx11](std::basic_string_view<char, std::char_traits<char> >) :103 42x 83.3% 71.4% 73.1% boost::corosio::detail::resolver_detail::flags_to_hints(boost::corosio::resolve_flags) :123 21x 100.0% 100.0% 100.0% boost::corosio::detail::resolver_detail::flags_to_ni_flags(boost::corosio::reverse_flags) :145 13x 100.0% 100.0% 100.0% boost::corosio::detail::resolver_detail::from_wide[abi:cxx11](std::basic_string_view<wchar_t, std::char_traits<wchar_t> >) :163 22x 83.3% 71.4% 73.1% boost::corosio::detail::resolver_detail::convert_results(addrinfoexW*, std::basic_string_view<char, std::char_traits<char> >, std::basic_string_view<char, std::char_traits<char> >) :185 16x 100.0% 88.9% 73.1% boost::corosio::detail::resolve_op::completion(unsigned long, unsigned long, _OVERLAPPED*) :214 9x 100.0% 50.0% 83.3% boost::corosio::detail::resolve_op::resolve_op() :222 39x 100.0% 100.0% boost::corosio::detail::resolve_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int) :225 21x 76.9% 76.0% 84.6% boost::corosio::detail::reverse_resolve_op::reverse_resolve_op() :279 39x 100.0% 100.0% boost::corosio::detail::reverse_resolve_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int) :285 13x 88.9% 83.3% 94.3% boost::corosio::detail::win_resolver::win_resolver(boost::corosio::detail::win_resolver_service&) :324 39x 100.0% 100.0% boost::corosio::detail::win_resolver::resolve(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref, std::basic_string_view<char, std::char_traits<char> >, std::basic_string_view<char, std::char_traits<char> >, boost::corosio::resolve_flags, std::stop_token, std::error_code*, boost::corosio::resolver_results*) :330 21x 100.0% 86.7% 89.2% boost::corosio::detail::win_resolver::reverse_resolve(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref, boost::corosio::endpoint const&, boost::corosio::reverse_flags, std::stop_token, std::error_code*, boost::corosio::reverse_resolver_result*) :389 13x 81.0% 66.7% 80.0% boost::corosio::detail::win_resolver::cancel() :429 43x 83.3% 50.0% 80.0% boost::corosio::detail::win_resolver::do_reverse_resolve_work(boost::corosio::detail::pool_work_item*) :441 13x 100.0% 100.0% 100.0% boost::corosio::detail::win_resolver_service::win_resolver_service(boost::capy::execution_context&, boost::corosio::detail::scheduler&) :495 567x 100.0% 100.0% 64.3% boost::corosio::detail::win_resolver_service::~win_resolver_service() :502 1134x 100.0% 100.0% boost::corosio::detail::win_resolver_service::shutdown() :505 567x 71.4% 50.0% 75.0% boost::corosio::detail::win_resolver_service::construct() :523 39x 100.0% 100.0% 70.6% boost::corosio::detail::win_resolver_service::destroy_impl(boost::corosio::detail::win_resolver&) :538 39x 100.0% 100.0% 66.7% boost::corosio::detail::win_resolver_service::post(boost::corosio::detail::overlapped_op*) :546 34x 100.0% 100.0% boost::corosio::detail::win_resolver_service::work_started() :552 34x 100.0% 100.0% boost::corosio::detail::win_resolver_service::work_finished() :558 34x 100.0% 100.0%
Line Branch TLA Hits Source Code
1 //
2 // Copyright (c) 2025 Vinnie Falco ([email protected])
3 // Copyright (c) 2026 Steve Gerbino
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_IOCP_WIN_RESOLVER_SERVICE_HPP
12 #define BOOST_COROSIO_NATIVE_DETAIL_IOCP_WIN_RESOLVER_SERVICE_HPP
13
14 #include <boost/corosio/detail/platform.hpp>
15
16 #if BOOST_COROSIO_HAS_IOCP
17
18 #include <boost/corosio/native/detail/iocp/win_resolver.hpp>
19 #include <boost/corosio/detail/thread_pool.hpp>
20
21 #include <unordered_map>
22
23 namespace boost::corosio::detail {
24
25 /** Windows IOCP resolver management service.
26
27 This service owns all resolver implementations and coordinates their
28 lifecycle. It provides:
29
30 - Resolver implementation allocation and deallocation
31 - Async DNS resolution via GetAddrInfoExW
32 - Graceful shutdown - destroys all implementations when io_context stops
33
34 @par Thread Safety
35 All public member functions are thread-safe.
36
37 @note Only available on Windows platforms with _WIN32_WINNT >= 0x0602.
38 */
39 class BOOST_COROSIO_DECL win_resolver_service final
40 : private win_wsa_init
41 , public capy::execution_context::service
42 , public io_object::io_service
43 {
44 public:
45 using key_type = win_resolver_service;
46
47 io_object::implementation* construct() override;
48
49 39x void destroy(io_object::implementation* p) override
50 {
51 39x auto& impl = static_cast<win_resolver&>(*p);
52 39x impl.cancel();
53 39x destroy_impl(impl);
54 39x }
55
56 /** Construct the resolver service.
57
58 @param ctx Reference to the owning execution_context.
59 @param sched Reference to the scheduler for posting completions.
60 */
61 win_resolver_service(capy::execution_context& ctx, scheduler& sched);
62
63 /** Destroy the resolver service. */
64 ~win_resolver_service();
65
66 win_resolver_service(win_resolver_service const&) = delete;
67 win_resolver_service& operator=(win_resolver_service const&) = delete;
68
69 /** Shut down the service. */
70 void shutdown() override;
71
72 /** Destroy a resolver implementation. */
73 void destroy_impl(win_resolver& impl);
74
75 /** Post an operation for completion. */
76 void post(overlapped_op* op);
77
78 /** Notify scheduler of pending I/O work. */
79 void work_started() noexcept;
80
81 /** Notify scheduler that I/O work completed. */
82 void work_finished() noexcept;
83
84 /** Return the resolver thread pool. */
85 13x thread_pool& pool() noexcept
86 {
87 13x return pool_;
88 }
89
90 private:
91 scheduler& sched_;
92 thread_pool& pool_;
93 win_mutex mutex_;
94 intrusive_list<win_resolver> resolver_list_;
95 std::unordered_map<win_resolver*, std::shared_ptr<win_resolver>>
96 resolver_ptrs_;
97 };
98
99 namespace resolver_detail {
100
101 // Convert narrow string to wide string
102 inline std::wstring
103 42x to_wide(std::string_view s)
104 {
105
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 42 times.
42x if (s.empty())
106 return {};
107
108
1/1
✓ Branch 7 → 8 taken 42 times.
42x int len = ::MultiByteToWideChar(
109 42x CP_UTF8, 0, s.data(), static_cast<int>(s.size()), nullptr, 0);
110
111
1/2
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 42 times.
42x if (len <= 0)
112 return {};
113
114
1/1
✓ Branch 12 → 13 taken 42 times.
42x std::wstring result(static_cast<std::size_t>(len), L'\0');
115
1/1
✓ Branch 17 → 18 taken 42 times.
84x ::MultiByteToWideChar(
116 42x CP_UTF8, 0, s.data(), static_cast<int>(s.size()), result.data(), len);
117
118 42x return result;
119 42x }
120
121 // Convert resolve_flags to ADDRINFOEXW hints
122 inline int
123 21x flags_to_hints(resolve_flags flags)
124 {
125 21x int hints = 0;
126
127
2/2
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 5 taken 20 times.
21x if ((flags & resolve_flags::passive) != resolve_flags::none)
128 1x hints |= AI_PASSIVE;
129
2/2
✓ Branch 6 → 7 taken 12 times.
✓ Branch 6 → 8 taken 9 times.
21x if ((flags & resolve_flags::numeric_host) != resolve_flags::none)
130 12x hints |= AI_NUMERICHOST;
131
2/2
✓ Branch 9 → 10 taken 9 times.
✓ Branch 9 → 11 taken 12 times.
21x if ((flags & resolve_flags::numeric_service) != resolve_flags::none)
132 9x hints |= AI_NUMERICSERV;
133
2/2
✓ Branch 12 → 13 taken 1 time.
✓ Branch 12 → 14 taken 20 times.
21x if ((flags & resolve_flags::address_configured) != resolve_flags::none)
134 1x hints |= AI_ADDRCONFIG;
135
2/2
✓ Branch 15 → 16 taken 1 time.
✓ Branch 15 → 17 taken 20 times.
21x if ((flags & resolve_flags::v4_mapped) != resolve_flags::none)
136 1x hints |= AI_V4MAPPED;
137
2/2
✓ Branch 18 → 19 taken 1 time.
✓ Branch 18 → 20 taken 20 times.
21x if ((flags & resolve_flags::all_matching) != resolve_flags::none)
138 1x hints |= AI_ALL;
139
140 21x return hints;
141 }
142
143 // Convert reverse_flags to getnameinfo NI_* flags
144 inline int
145 13x flags_to_ni_flags(reverse_flags flags)
146 {
147 13x int ni_flags = 0;
148
149
2/2
✓ Branch 3 → 4 taken 6 times.
✓ Branch 3 → 5 taken 7 times.
13x if ((flags & reverse_flags::numeric_host) != reverse_flags::none)
150 6x ni_flags |= NI_NUMERICHOST;
151
2/2
✓ Branch 6 → 7 taken 6 times.
✓ Branch 6 → 8 taken 7 times.
13x if ((flags & reverse_flags::numeric_service) != reverse_flags::none)
152 6x ni_flags |= NI_NUMERICSERV;
153
2/2
✓ Branch 9 → 10 taken 1 time.
✓ Branch 9 → 11 taken 12 times.
13x if ((flags & reverse_flags::name_required) != reverse_flags::none)
154 1x ni_flags |= NI_NAMEREQD;
155
2/2
✓ Branch 12 → 13 taken 1 time.
✓ Branch 12 → 14 taken 12 times.
13x if ((flags & reverse_flags::datagram_service) != reverse_flags::none)
156 1x ni_flags |= NI_DGRAM;
157
158 13x return ni_flags;
159 }
160
161 // Convert wide string to UTF-8 string
162 inline std::string
163 22x from_wide(std::wstring_view s)
164 {
165
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 22 times.
22x if (s.empty())
166 return {};
167
168
1/1
✓ Branch 7 → 8 taken 22 times.
22x int len = ::WideCharToMultiByte(
169 22x CP_UTF8, 0, s.data(), static_cast<int>(s.size()), nullptr, 0, nullptr,
170 nullptr);
171
172
1/2
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 22 times.
22x if (len <= 0)
173 return {};
174
175
1/1
✓ Branch 12 → 13 taken 22 times.
22x std::string result(static_cast<std::size_t>(len), '\0');
176
1/1
✓ Branch 17 → 18 taken 22 times.
44x ::WideCharToMultiByte(
177 22x CP_UTF8, 0, s.data(), static_cast<int>(s.size()), result.data(), len,
178 nullptr, nullptr);
179
180 22x return result;
181 22x }
182
183 // Convert ADDRINFOEXW results to resolver_results
184 inline resolver_results
185 16x convert_results(
186 ADDRINFOEXW* ai, std::string_view host, std::string_view service)
187 {
188 16x std::vector<resolver_entry> entries;
189
190
2/2
✓ Branch 12 → 3 taken 22 times.
✓ Branch 12 → 13 taken 16 times.
38x for (auto* p = ai; p != nullptr; p = p->ai_next)
191 {
192
2/2
✓ Branch 3 → 4 taken 14 times.
✓ Branch 3 → 7 taken 8 times.
22x if (p->ai_family == AF_INET)
193 {
194 14x auto* addr = reinterpret_cast<sockaddr_in*>(p->ai_addr);
195 14x auto ep = from_sockaddr_in(*addr);
196
1/1
✓ Branch 5 → 6 taken 14 times.
14x entries.emplace_back(ep, host, service);
197 }
198
1/2
✓ Branch 7 → 8 taken 8 times.
✗ Branch 7 → 11 not taken.
8x else if (p->ai_family == AF_INET6)
199 {
200 8x auto* addr = reinterpret_cast<sockaddr_in6*>(p->ai_addr);
201 8x auto ep = from_sockaddr_in6(*addr);
202
1/1
✓ Branch 9 → 10 taken 8 times.
8x entries.emplace_back(ep, host, service);
203 }
204 }
205
206
1/1
✓ Branch 15 → 16 taken 16 times.
32x return resolver_results(std::move(entries));
207 16x }
208
209 } // namespace resolver_detail
210
211 // resolve_op
212
213 inline void CALLBACK
214 9x resolve_op::completion(DWORD dwError, DWORD /*bytes*/, OVERLAPPED* ov)
215 {
216
1/2
✓ Branch 2 → 3 taken 9 times.
✗ Branch 2 → 4 not taken.
9x auto* op = static_cast<resolve_op*>(ov);
217 9x op->dwError = dwError;
218 9x op->impl->svc_.work_finished();
219 9x op->impl->svc_.post(op);
220 9x }
221
222 39x inline resolve_op::resolve_op() noexcept : overlapped_op(&do_complete) {}
223
224 inline void
225 21x resolve_op::do_complete(
226 void* owner,
227 scheduler_op* base,
228 std::uint32_t /*bytes*/,
229 std::uint32_t /*error*/)
230 {
231 21x auto* op = static_cast<resolve_op*>(base);
232
233
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 8 taken 21 times.
21x if (!owner)
234 {
235 // Destroy path
236 op->stop_cb.reset();
237 if (op->results)
238 {
239 ::FreeAddrInfoExW(op->results);
240 op->results = nullptr;
241 }
242 op->cancel_handle = nullptr;
243 return;
244 }
245
246 21x op->stop_cb.reset();
247
248
1/2
✓ Branch 9 → 10 taken 21 times.
✗ Branch 9 → 18 not taken.
21x if (op->ec_out)
249 {
250
2/2
✓ Branch 11 → 12 taken 1 time.
✓ Branch 11 → 14 taken 20 times.
21x if (op->cancelled.load(std::memory_order_acquire))
251 1x *op->ec_out = capy::error::canceled;
252
2/2
✓ Branch 14 → 15 taken 4 times.
✓ Branch 14 → 16 taken 16 times.
20x else if (op->dwError != 0)
253 4x *op->ec_out = make_err(op->dwError);
254 else
255 16x *op->ec_out = {};
256 }
257
258
2/2
✓ Branch 20 → 21 taken 20 times.
✓ Branch 20 → 24 taken 1 time.
21x if (op->out && !op->cancelled.load(std::memory_order_acquire) &&
259
6/8
✓ Branch 18 → 19 taken 21 times.
✗ Branch 18 → 24 not taken.
✓ Branch 21 → 22 taken 16 times.
✓ Branch 21 → 24 taken 4 times.
✓ Branch 22 → 23 taken 16 times.
✗ Branch 22 → 24 not taken.
✓ Branch 25 → 26 taken 16 times.
✓ Branch 25 → 32 taken 5 times.
42x op->dwError == 0 && op->results)
260 {
261
1/1
✓ Branch 28 → 29 taken 16 times.
32x *op->out = resolver_detail::convert_results(
262 16x op->results, op->host, op->service);
263 }
264
265
2/2
✓ Branch 32 → 33 taken 17 times.
✓ Branch 32 → 35 taken 4 times.
21x if (op->results)
266 {
267 17x ::FreeAddrInfoExW(op->results);
268 17x op->results = nullptr;
269 }
270
271 21x op->cancel_handle = nullptr;
272
273 21x op->cont_op.cont.h = op->h;
274
2/2
✓ Branch 35 → 36 taken 21 times.
✓ Branch 36 → 37 taken 21 times.
21x dispatch_coro(op->ex, op->cont_op.cont).resume();
275 }
276
277 // reverse_resolve_op
278
279 39x inline reverse_resolve_op::reverse_resolve_op() noexcept
280 39x : overlapped_op(&do_complete)
281 {
282 39x }
283
284 inline void
285 13x reverse_resolve_op::do_complete(
286 void* owner,
287 scheduler_op* base,
288 std::uint32_t /*bytes*/,
289 std::uint32_t /*error*/)
290 {
291 13x auto* op = static_cast<reverse_resolve_op*>(base);
292
293
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 5 taken 13 times.
13x if (!owner)
294 {
295 op->stop_cb.reset();
296 return;
297 }
298
299 13x op->stop_cb.reset();
300
301
1/2
✓ Branch 6 → 7 taken 13 times.
✗ Branch 6 → 15 not taken.
13x if (op->ec_out)
302 {
303
2/2
✓ Branch 8 → 9 taken 1 time.
✓ Branch 8 → 11 taken 12 times.
13x if (op->cancelled.load(std::memory_order_acquire))
304 1x *op->ec_out = capy::error::canceled;
305
2/2
✓ Branch 11 → 12 taken 1 time.
✓ Branch 11 → 13 taken 11 times.
12x else if (op->gai_error != 0)
306 1x *op->ec_out = make_err(static_cast<DWORD>(op->gai_error));
307 else
308 11x *op->ec_out = {};
309 }
310
311
5/6
✓ Branch 15 → 16 taken 13 times.
✗ Branch 15 → 20 not taken.
✓ Branch 17 → 18 taken 12 times.
✓ Branch 17 → 20 taken 1 time.
✓ Branch 21 → 22 taken 11 times.
✓ Branch 21 → 32 taken 2 times.
25x if (op->result_out && !op->cancelled.load(std::memory_order_acquire) &&
312
2/2
✓ Branch 18 → 19 taken 11 times.
✓ Branch 18 → 20 taken 1 time.
12x op->gai_error == 0)
313 {
314 11x *op->result_out = reverse_resolver_result(
315 22x op->ep, std::move(op->stored_host), std::move(op->stored_service));
316 }
317
318 13x op->cont_op.cont.h = op->h;
319
2/2
✓ Branch 32 → 33 taken 13 times.
✓ Branch 33 → 34 taken 13 times.
13x dispatch_coro(op->ex, op->cont_op.cont).resume();
320 }
321
322 // win_resolver
323
324 39x inline win_resolver::win_resolver(win_resolver_service& svc) noexcept
325 39x : svc_(svc)
326 {
327 39x }
328
329 inline std::coroutine_handle<>
330 21x win_resolver::resolve(
331 std::coroutine_handle<> h,
332 capy::executor_ref d,
333 std::string_view host,
334 std::string_view service,
335 resolve_flags flags,
336 std::stop_token token,
337 std::error_code* ec,
338 resolver_results* out)
339 {
340 21x auto& op = op_;
341 21x op.reset();
342 21x op.h = h;
343 21x op.ex = d;
344 21x op.ec_out = ec;
345 21x op.out = out;
346 21x op.impl = this;
347
1/1
✓ Branch 3 → 4 taken 21 times.
21x op.host = host;
348
1/1
✓ Branch 4 → 5 taken 21 times.
21x op.service = service;
349
1/1
✓ Branch 5 → 6 taken 21 times.
21x op.host_w = resolver_detail::to_wide(host);
350
1/1
✓ Branch 8 → 9 taken 21 times.
21x op.service_w = resolver_detail::to_wide(service);
351 21x op.start(token);
352
353 21x ADDRINFOEXW hints{};
354 21x hints.ai_family = AF_UNSPEC;
355 21x hints.ai_socktype = SOCK_STREAM;
356 21x hints.ai_flags = resolver_detail::flags_to_hints(flags);
357
358 // Keep io_context alive while resolution is pending
359 21x svc_.work_started();
360
361
3/5
✗ Branch 17 → 18 not taken.
✓ Branch 17 → 19 taken 21 times.
✗ Branch 21 → 22 not taken.
✓ Branch 21 → 23 taken 21 times.
✓ Branch 24 → 25 taken 21 times.
63x int result = ::GetAddrInfoExW(
362 42x op.host_w.empty() ? nullptr : op.host_w.c_str(),
363 42x op.service_w.empty() ? nullptr : op.service_w.c_str(), NS_DNS, nullptr,
364 &hints, &op.results, nullptr, &op, &resolve_op::completion,
365 &op.cancel_handle);
366
367
2/2
✓ Branch 25 → 26 taken 12 times.
✓ Branch 25 → 32 taken 9 times.
21x if (result != WSA_IO_PENDING)
368 {
369 // Completed synchronously - callback won't be invoked
370 12x svc_.work_finished();
371
372
2/2
✓ Branch 27 → 28 taken 9 times.
✓ Branch 27 → 29 taken 3 times.
12x if (result == 0)
373 {
374 // Completed synchronously
375 9x op.dwError = 0;
376 }
377 else
378 {
379
1/1
✓ Branch 29 → 30 taken 3 times.
3x op.dwError = static_cast<DWORD>(::WSAGetLastError());
380 }
381
382
1/1
✓ Branch 31 → 32 taken 12 times.
12x svc_.post(&op);
383 }
384 // completion is always posted to scheduler queue, never inline.
385 21x return std::noop_coroutine();
386 }
387
388 inline std::coroutine_handle<>
389 13x win_resolver::reverse_resolve(
390 std::coroutine_handle<> h,
391 capy::executor_ref d,
392 endpoint const& ep,
393 reverse_flags flags,
394 std::stop_token token,
395 std::error_code* ec,
396 reverse_resolver_result* result_out)
397 {
398 13x auto& op = reverse_op_;
399 13x op.reset();
400 13x op.h = h;
401 13x op.ex = d;
402 13x op.ec_out = ec;
403 13x op.result_out = result_out;
404 13x op.impl = this;
405 13x op.ep = ep;
406 13x op.flags = flags;
407 13x op.start(token);
408
409 // Keep io_context alive while resolution is pending
410 13x svc_.work_started();
411
412 // Prevent impl destruction while work is in flight
413 13x reverse_pool_op_.resolver_ = this;
414
1/1
✓ Branch 7 → 8 taken 13 times.
13x reverse_pool_op_.ref_ = this->shared_from_this();
415 13x reverse_pool_op_.func_ = &win_resolver::do_reverse_resolve_work;
416
1/2
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 17 taken 13 times.
13x if (!svc_.pool().post(&reverse_pool_op_))
417 {
418 // Pool shut down — complete with cancellation
419 reverse_pool_op_.ref_.reset();
420 op.cancelled.store(true, std::memory_order_release);
421 svc_.work_finished();
422 svc_.post(&reverse_op_);
423 }
424 // completion is always posted to scheduler queue, never inline.
425 13x return std::noop_coroutine();
426 }
427
428 inline void
429 43x win_resolver::cancel() noexcept
430 {
431 43x op_.request_cancel();
432 43x reverse_op_.request_cancel();
433
434
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 43 times.
43x if (op_.cancel_handle)
435 {
436 ::GetAddrInfoExCancel(&op_.cancel_handle);
437 }
438 43x }
439
440 inline void
441 13x win_resolver::do_reverse_resolve_work(pool_work_item* w) noexcept
442 {
443 13x auto* pw = static_cast<pool_op*>(w);
444 13x auto* self = pw->resolver_;
445
446 13x sockaddr_storage ss{};
447 int ss_len;
448
449
2/2
✓ Branch 3 → 4 taken 11 times.
✓ Branch 3 → 6 taken 2 times.
13x if (self->reverse_op_.ep.is_v4())
450 {
451 11x auto sa = to_sockaddr_in(self->reverse_op_.ep);
452 11x std::memcpy(&ss, &sa, sizeof(sa));
453 11x ss_len = sizeof(sockaddr_in);
454 }
455 else
456 {
457 2x auto sa = to_sockaddr_in6(self->reverse_op_.ep);
458 2x std::memcpy(&ss, &sa, sizeof(sa));
459 2x ss_len = sizeof(sockaddr_in6);
460 }
461
462 wchar_t host[NI_MAXHOST];
463 wchar_t service[NI_MAXSERV];
464
465 13x int result = ::GetNameInfoW(
466 reinterpret_cast<sockaddr*>(&ss), ss_len, host, NI_MAXHOST, service,
467 NI_MAXSERV,
468 resolver_detail::flags_to_ni_flags(self->reverse_op_.flags));
469
470
2/2
✓ Branch 11 → 12 taken 12 times.
✓ Branch 11 → 23 taken 1 time.
13x if (!self->reverse_op_.cancelled.load(std::memory_order_acquire))
471 {
472
2/2
✓ Branch 12 → 13 taken 11 times.
✓ Branch 12 → 22 taken 1 time.
12x if (result == 0)
473 {
474 11x self->reverse_op_.stored_host = resolver_detail::from_wide(host);
475 self->reverse_op_.stored_service =
476 11x resolver_detail::from_wide(service);
477 11x self->reverse_op_.gai_error = 0;
478 }
479 else
480 {
481 1x self->reverse_op_.gai_error = result;
482 }
483 }
484
485 13x self->svc_.work_finished();
486
487 // Move ref to stack before post — post may trigger destroy_impl
488 // which erases the last shared_ptr, destroying *self (and *pw)
489 13x auto ref = std::move(pw->ref_);
490 13x self->svc_.post(&self->reverse_op_);
491 13x }
492
493 // win_resolver_service
494
495 567x inline win_resolver_service::win_resolver_service(
496 567x capy::execution_context& ctx, scheduler& sched)
497 567x : sched_(sched)
498
2/2
✓ Branch 5 → 6 taken 567 times.
✓ Branch 6 → 7 taken 567 times.
567x , pool_(ctx.use_service<thread_pool>())
499 {
500 567x }
501
502 1134x inline win_resolver_service::~win_resolver_service() {}
503
504 inline void
505 567x win_resolver_service::shutdown()
506 {
507 567x std::lock_guard<win_mutex> lock(mutex_);
508
509 // Cancel all resolvers (sets cancelled flag checked by pool threads)
510
1/2
✗ Branch 6 → 4 not taken.
✓ Branch 6 → 7 taken 567 times.
567x for (auto* impl = resolver_list_.pop_front(); impl != nullptr;
511 impl = resolver_list_.pop_front())
512 {
513 impl->cancel();
514 }
515
516 // Clear the map which releases shared_ptrs.
517 // The thread pool service shuts down separately via
518 // execution_context service ordering.
519 567x resolver_ptrs_.clear();
520 567x }
521
522 inline io_object::implementation*
523 39x win_resolver_service::construct()
524 {
525
1/1
✓ Branch 2 → 3 taken 39 times.
39x auto ptr = std::make_shared<win_resolver>(*this);
526 39x auto* impl = ptr.get();
527
528 {
529 39x std::lock_guard<win_mutex> lock(mutex_);
530 39x resolver_list_.push_back(impl);
531
1/1
✓ Branch 7 → 8 taken 39 times.
39x resolver_ptrs_[impl] = std::move(ptr);
532 39x }
533
534 39x return impl;
535 39x }
536
537 inline void
538 39x win_resolver_service::destroy_impl(win_resolver& impl)
539 {
540 39x std::lock_guard<win_mutex> lock(mutex_);
541 39x resolver_list_.remove(&impl);
542
1/1
✓ Branch 4 → 5 taken 39 times.
39x resolver_ptrs_.erase(&impl);
543 39x }
544
545 inline void
546 34x win_resolver_service::post(overlapped_op* op)
547 {
548 34x sched_.post(op);
549 34x }
550
551 inline void
552 34x win_resolver_service::work_started() noexcept
553 {
554 34x sched_.work_started();
555 34x }
556
557 inline void
558 34x win_resolver_service::work_finished() noexcept
559 {
560 34x sched_.work_finished();
561 34x }
562
563 } // namespace boost::corosio::detail
564
565 #endif // BOOST_COROSIO_HAS_IOCP
566
567 #endif // BOOST_COROSIO_NATIVE_DETAIL_IOCP_WIN_RESOLVER_SERVICE_HPP
568