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

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