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

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