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

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