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