LCOV - code coverage report
Current view: top level - corosio/native/detail/posix - posix_resolver_service.hpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 82.4 % 278 229 49
Test Date: 2026-03-17 15:52:49 Functions: 93.5 % 31 29 2

           TLA  Line data    Source code
       1                 : //
       2                 : // Copyright (c) 2026 Steve Gerbino
       3                 : //
       4                 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
       5                 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
       6                 : //
       7                 : // Official repository: https://github.com/cppalliance/corosio
       8                 : //
       9                 : 
      10                 : #ifndef BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_RESOLVER_SERVICE_HPP
      11                 : #define BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_RESOLVER_SERVICE_HPP
      12                 : 
      13                 : #include <boost/corosio/detail/platform.hpp>
      14                 : 
      15                 : #if BOOST_COROSIO_POSIX
      16                 : 
      17                 : #include <boost/corosio/native/detail/posix/posix_resolver.hpp>
      18                 : #include <boost/corosio/detail/thread_pool.hpp>
      19                 : 
      20                 : #include <unordered_map>
      21                 : 
      22                 : namespace boost::corosio::detail {
      23                 : 
      24                 : /** Resolver service for POSIX backends.
      25                 : 
      26                 :     Owns all posix_resolver instances. Thread lifecycle is managed
      27                 :     by the thread_pool service.
      28                 : */
      29                 : class BOOST_COROSIO_DECL posix_resolver_service final
      30                 :     : public capy::execution_context::service
      31                 :     , public io_object::io_service
      32                 : {
      33                 : public:
      34                 :     using key_type = posix_resolver_service;
      35                 : 
      36 HIT         466 :     posix_resolver_service(capy::execution_context& ctx, scheduler& sched)
      37             932 :         : sched_(&sched)
      38             466 :         , pool_(ctx.make_service<thread_pool>())
      39                 :     {
      40             466 :     }
      41                 : 
      42             932 :     ~posix_resolver_service() override = default;
      43                 : 
      44                 :     posix_resolver_service(posix_resolver_service const&)            = delete;
      45                 :     posix_resolver_service& operator=(posix_resolver_service const&) = delete;
      46                 : 
      47                 :     io_object::implementation* construct() override;
      48                 : 
      49              29 :     void destroy(io_object::implementation* p) override
      50                 :     {
      51              29 :         auto& impl = static_cast<posix_resolver&>(*p);
      52              29 :         impl.cancel();
      53              29 :         destroy_impl(impl);
      54              29 :     }
      55                 : 
      56                 :     void shutdown() override;
      57                 :     void destroy_impl(posix_resolver& impl);
      58                 : 
      59                 :     void post(scheduler_op* op);
      60                 :     void work_started() noexcept;
      61                 :     void work_finished() noexcept;
      62                 : 
      63                 :     /** Return the resolver thread pool. */
      64              26 :     thread_pool& pool() noexcept
      65                 :     {
      66              26 :         return pool_;
      67                 :     }
      68                 : 
      69                 : private:
      70                 :     scheduler* sched_;
      71                 :     thread_pool& pool_;
      72                 :     std::mutex mutex_;
      73                 :     intrusive_list<posix_resolver> resolver_list_;
      74                 :     std::unordered_map<posix_resolver*, std::shared_ptr<posix_resolver>>
      75                 :         resolver_ptrs_;
      76                 : };
      77                 : 
      78                 : /** Get or create the resolver service for the given context.
      79                 : 
      80                 :     This function is called by the concrete scheduler during initialization
      81                 :     to create the resolver service with a reference to itself.
      82                 : 
      83                 :     @param ctx Reference to the owning execution_context.
      84                 :     @param sched Reference to the scheduler for posting completions.
      85                 :     @return Reference to the resolver service.
      86                 : */
      87                 : posix_resolver_service&
      88                 : get_resolver_service(capy::execution_context& ctx, scheduler& sched);
      89                 : 
      90                 : // ---------------------------------------------------------------------------
      91                 : // Inline implementation
      92                 : // ---------------------------------------------------------------------------
      93                 : 
      94                 : // posix_resolver_detail helpers
      95                 : 
      96                 : inline int
      97              16 : posix_resolver_detail::flags_to_hints(resolve_flags flags)
      98                 : {
      99              16 :     int hints = 0;
     100                 : 
     101              16 :     if ((flags & resolve_flags::passive) != resolve_flags::none)
     102 MIS           0 :         hints |= AI_PASSIVE;
     103 HIT          16 :     if ((flags & resolve_flags::numeric_host) != resolve_flags::none)
     104              11 :         hints |= AI_NUMERICHOST;
     105              16 :     if ((flags & resolve_flags::numeric_service) != resolve_flags::none)
     106               8 :         hints |= AI_NUMERICSERV;
     107              16 :     if ((flags & resolve_flags::address_configured) != resolve_flags::none)
     108 MIS           0 :         hints |= AI_ADDRCONFIG;
     109 HIT          16 :     if ((flags & resolve_flags::v4_mapped) != resolve_flags::none)
     110 MIS           0 :         hints |= AI_V4MAPPED;
     111 HIT          16 :     if ((flags & resolve_flags::all_matching) != resolve_flags::none)
     112 MIS           0 :         hints |= AI_ALL;
     113                 : 
     114 HIT          16 :     return hints;
     115                 : }
     116                 : 
     117                 : inline int
     118              10 : posix_resolver_detail::flags_to_ni_flags(reverse_flags flags)
     119                 : {
     120              10 :     int ni_flags = 0;
     121                 : 
     122              10 :     if ((flags & reverse_flags::numeric_host) != reverse_flags::none)
     123               5 :         ni_flags |= NI_NUMERICHOST;
     124              10 :     if ((flags & reverse_flags::numeric_service) != reverse_flags::none)
     125               5 :         ni_flags |= NI_NUMERICSERV;
     126              10 :     if ((flags & reverse_flags::name_required) != reverse_flags::none)
     127               1 :         ni_flags |= NI_NAMEREQD;
     128              10 :     if ((flags & reverse_flags::datagram_service) != reverse_flags::none)
     129 MIS           0 :         ni_flags |= NI_DGRAM;
     130                 : 
     131 HIT          10 :     return ni_flags;
     132                 : }
     133                 : 
     134                 : inline resolver_results
     135              13 : posix_resolver_detail::convert_results(
     136                 :     struct addrinfo* ai, std::string_view host, std::string_view service)
     137                 : {
     138              13 :     std::vector<resolver_entry> entries;
     139              13 :     entries.reserve(4); // Most lookups return 1-4 addresses
     140                 : 
     141              26 :     for (auto* p = ai; p != nullptr; p = p->ai_next)
     142                 :     {
     143              13 :         if (p->ai_family == AF_INET)
     144                 :         {
     145              11 :             auto* addr = reinterpret_cast<sockaddr_in*>(p->ai_addr);
     146              11 :             auto ep    = from_sockaddr_in(*addr);
     147              11 :             entries.emplace_back(ep, host, service);
     148                 :         }
     149               2 :         else if (p->ai_family == AF_INET6)
     150                 :         {
     151               2 :             auto* addr = reinterpret_cast<sockaddr_in6*>(p->ai_addr);
     152               2 :             auto ep    = from_sockaddr_in6(*addr);
     153               2 :             entries.emplace_back(ep, host, service);
     154                 :         }
     155                 :     }
     156                 : 
     157              26 :     return resolver_results(std::move(entries));
     158              13 : }
     159                 : 
     160                 : inline std::error_code
     161               4 : posix_resolver_detail::make_gai_error(int gai_err)
     162                 : {
     163                 :     // Map GAI errors to appropriate generic error codes
     164               4 :     switch (gai_err)
     165                 :     {
     166 MIS           0 :     case EAI_AGAIN:
     167                 :         // Temporary failure - try again later
     168               0 :         return std::error_code(
     169                 :             static_cast<int>(std::errc::resource_unavailable_try_again),
     170               0 :             std::generic_category());
     171                 : 
     172               0 :     case EAI_BADFLAGS:
     173                 :         // Invalid flags
     174               0 :         return std::error_code(
     175                 :             static_cast<int>(std::errc::invalid_argument),
     176               0 :             std::generic_category());
     177                 : 
     178               0 :     case EAI_FAIL:
     179                 :         // Non-recoverable failure
     180               0 :         return std::error_code(
     181               0 :             static_cast<int>(std::errc::io_error), std::generic_category());
     182                 : 
     183               0 :     case EAI_FAMILY:
     184                 :         // Address family not supported
     185               0 :         return std::error_code(
     186                 :             static_cast<int>(std::errc::address_family_not_supported),
     187               0 :             std::generic_category());
     188                 : 
     189               0 :     case EAI_MEMORY:
     190                 :         // Memory allocation failure
     191               0 :         return std::error_code(
     192                 :             static_cast<int>(std::errc::not_enough_memory),
     193               0 :             std::generic_category());
     194                 : 
     195 HIT           4 :     case EAI_NONAME:
     196                 :         // Host or service not found
     197               4 :         return std::error_code(
     198                 :             static_cast<int>(std::errc::no_such_device_or_address),
     199               4 :             std::generic_category());
     200                 : 
     201 MIS           0 :     case EAI_SERVICE:
     202                 :         // Service not supported for socket type
     203               0 :         return std::error_code(
     204                 :             static_cast<int>(std::errc::invalid_argument),
     205               0 :             std::generic_category());
     206                 : 
     207               0 :     case EAI_SOCKTYPE:
     208                 :         // Socket type not supported
     209               0 :         return std::error_code(
     210                 :             static_cast<int>(std::errc::not_supported),
     211               0 :             std::generic_category());
     212                 : 
     213               0 :     case EAI_SYSTEM:
     214                 :         // System error - use errno
     215               0 :         return std::error_code(errno, std::generic_category());
     216                 : 
     217               0 :     default:
     218                 :         // Unknown error
     219               0 :         return std::error_code(
     220               0 :             static_cast<int>(std::errc::io_error), std::generic_category());
     221                 :     }
     222                 : }
     223                 : 
     224                 : // posix_resolver
     225                 : 
     226 HIT          29 : inline posix_resolver::posix_resolver(posix_resolver_service& svc) noexcept
     227              29 :     : svc_(svc)
     228                 : {
     229              29 : }
     230                 : 
     231                 : // posix_resolver::resolve_op implementation
     232                 : 
     233                 : inline void
     234              16 : posix_resolver::resolve_op::reset() noexcept
     235                 : {
     236              16 :     host.clear();
     237              16 :     service.clear();
     238              16 :     flags          = resolve_flags::none;
     239              16 :     stored_results = resolver_results{};
     240              16 :     gai_error      = 0;
     241              16 :     cancelled.store(false, std::memory_order_relaxed);
     242              16 :     stop_cb.reset();
     243              16 :     ec_out = nullptr;
     244              16 :     out    = nullptr;
     245              16 : }
     246                 : 
     247                 : inline void
     248              16 : posix_resolver::resolve_op::operator()()
     249                 : {
     250              16 :     stop_cb.reset(); // Disconnect stop callback
     251                 : 
     252              16 :     bool const was_cancelled = cancelled.load(std::memory_order_acquire);
     253                 : 
     254              16 :     if (ec_out)
     255                 :     {
     256              16 :         if (was_cancelled)
     257 MIS           0 :             *ec_out = capy::error::canceled;
     258 HIT          16 :         else if (gai_error != 0)
     259               3 :             *ec_out = posix_resolver_detail::make_gai_error(gai_error);
     260                 :         else
     261              13 :             *ec_out = {}; // Clear on success
     262                 :     }
     263                 : 
     264              16 :     if (out && !was_cancelled && gai_error == 0)
     265              13 :         *out = std::move(stored_results);
     266                 : 
     267              16 :     impl->svc_.work_finished();
     268              16 :     dispatch_coro(ex, h).resume();
     269              16 : }
     270                 : 
     271                 : inline void
     272 MIS           0 : posix_resolver::resolve_op::destroy()
     273                 : {
     274               0 :     stop_cb.reset();
     275               0 : }
     276                 : 
     277                 : inline void
     278 HIT          33 : posix_resolver::resolve_op::request_cancel() noexcept
     279                 : {
     280              33 :     cancelled.store(true, std::memory_order_release);
     281              33 : }
     282                 : 
     283                 : inline void
     284              16 : posix_resolver::resolve_op::start(std::stop_token const& token)
     285                 : {
     286              16 :     cancelled.store(false, std::memory_order_release);
     287              16 :     stop_cb.reset();
     288                 : 
     289              16 :     if (token.stop_possible())
     290 MIS           0 :         stop_cb.emplace(token, canceller{this});
     291 HIT          16 : }
     292                 : 
     293                 : // posix_resolver::reverse_resolve_op implementation
     294                 : 
     295                 : inline void
     296              10 : posix_resolver::reverse_resolve_op::reset() noexcept
     297                 : {
     298              10 :     ep    = endpoint{};
     299              10 :     flags = reverse_flags::none;
     300              10 :     stored_host.clear();
     301              10 :     stored_service.clear();
     302              10 :     gai_error = 0;
     303              10 :     cancelled.store(false, std::memory_order_relaxed);
     304              10 :     stop_cb.reset();
     305              10 :     ec_out     = nullptr;
     306              10 :     result_out = nullptr;
     307              10 : }
     308                 : 
     309                 : inline void
     310              10 : posix_resolver::reverse_resolve_op::operator()()
     311                 : {
     312              10 :     stop_cb.reset(); // Disconnect stop callback
     313                 : 
     314              10 :     bool const was_cancelled = cancelled.load(std::memory_order_acquire);
     315                 : 
     316              10 :     if (ec_out)
     317                 :     {
     318              10 :         if (was_cancelled)
     319 MIS           0 :             *ec_out = capy::error::canceled;
     320 HIT          10 :         else if (gai_error != 0)
     321               1 :             *ec_out = posix_resolver_detail::make_gai_error(gai_error);
     322                 :         else
     323               9 :             *ec_out = {}; // Clear on success
     324                 :     }
     325                 : 
     326              10 :     if (result_out && !was_cancelled && gai_error == 0)
     327                 :     {
     328              27 :         *result_out = reverse_resolver_result(
     329              27 :             ep, std::move(stored_host), std::move(stored_service));
     330                 :     }
     331                 : 
     332              10 :     impl->svc_.work_finished();
     333              10 :     dispatch_coro(ex, h).resume();
     334              10 : }
     335                 : 
     336                 : inline void
     337 MIS           0 : posix_resolver::reverse_resolve_op::destroy()
     338                 : {
     339               0 :     stop_cb.reset();
     340               0 : }
     341                 : 
     342                 : inline void
     343 HIT          33 : posix_resolver::reverse_resolve_op::request_cancel() noexcept
     344                 : {
     345              33 :     cancelled.store(true, std::memory_order_release);
     346              33 : }
     347                 : 
     348                 : inline void
     349              10 : posix_resolver::reverse_resolve_op::start(std::stop_token const& token)
     350                 : {
     351              10 :     cancelled.store(false, std::memory_order_release);
     352              10 :     stop_cb.reset();
     353                 : 
     354              10 :     if (token.stop_possible())
     355 MIS           0 :         stop_cb.emplace(token, canceller{this});
     356 HIT          10 : }
     357                 : 
     358                 : // posix_resolver implementation
     359                 : 
     360                 : inline std::coroutine_handle<>
     361              16 : posix_resolver::resolve(
     362                 :     std::coroutine_handle<> h,
     363                 :     capy::executor_ref ex,
     364                 :     std::string_view host,
     365                 :     std::string_view service,
     366                 :     resolve_flags flags,
     367                 :     std::stop_token token,
     368                 :     std::error_code* ec,
     369                 :     resolver_results* out)
     370                 : {
     371              16 :     auto& op = op_;
     372              16 :     op.reset();
     373              16 :     op.h       = h;
     374              16 :     op.ex      = ex;
     375              16 :     op.impl    = this;
     376              16 :     op.ec_out  = ec;
     377              16 :     op.out     = out;
     378              16 :     op.host    = host;
     379              16 :     op.service = service;
     380              16 :     op.flags   = flags;
     381              16 :     op.start(token);
     382                 : 
     383                 :     // Keep io_context alive while resolution is pending
     384              16 :     op.ex.on_work_started();
     385                 : 
     386                 :     // Prevent impl destruction while work is in flight
     387              16 :     resolve_pool_op_.resolver_ = this;
     388              16 :     resolve_pool_op_.ref_      = this->shared_from_this();
     389              16 :     resolve_pool_op_.func_     = &posix_resolver::do_resolve_work;
     390              16 :     if (!svc_.pool().post(&resolve_pool_op_))
     391                 :     {
     392                 :         // Pool shut down — complete with cancellation
     393 MIS           0 :         resolve_pool_op_.ref_.reset();
     394               0 :         op.cancelled.store(true, std::memory_order_release);
     395               0 :         svc_.post(&op_);
     396                 :     }
     397 HIT          16 :     return std::noop_coroutine();
     398                 : }
     399                 : 
     400                 : inline std::coroutine_handle<>
     401              10 : posix_resolver::reverse_resolve(
     402                 :     std::coroutine_handle<> h,
     403                 :     capy::executor_ref ex,
     404                 :     endpoint const& ep,
     405                 :     reverse_flags flags,
     406                 :     std::stop_token token,
     407                 :     std::error_code* ec,
     408                 :     reverse_resolver_result* result_out)
     409                 : {
     410              10 :     auto& op = reverse_op_;
     411              10 :     op.reset();
     412              10 :     op.h          = h;
     413              10 :     op.ex         = ex;
     414              10 :     op.impl       = this;
     415              10 :     op.ec_out     = ec;
     416              10 :     op.result_out = result_out;
     417              10 :     op.ep         = ep;
     418              10 :     op.flags      = flags;
     419              10 :     op.start(token);
     420                 : 
     421                 :     // Keep io_context alive while resolution is pending
     422              10 :     op.ex.on_work_started();
     423                 : 
     424                 :     // Prevent impl destruction while work is in flight
     425              10 :     reverse_pool_op_.resolver_ = this;
     426              10 :     reverse_pool_op_.ref_      = this->shared_from_this();
     427              10 :     reverse_pool_op_.func_     = &posix_resolver::do_reverse_resolve_work;
     428              10 :     if (!svc_.pool().post(&reverse_pool_op_))
     429                 :     {
     430                 :         // Pool shut down — complete with cancellation
     431 MIS           0 :         reverse_pool_op_.ref_.reset();
     432               0 :         op.cancelled.store(true, std::memory_order_release);
     433               0 :         svc_.post(&reverse_op_);
     434                 :     }
     435 HIT          10 :     return std::noop_coroutine();
     436                 : }
     437                 : 
     438                 : inline void
     439              33 : posix_resolver::cancel() noexcept
     440                 : {
     441              33 :     op_.request_cancel();
     442              33 :     reverse_op_.request_cancel();
     443              33 : }
     444                 : 
     445                 : inline void
     446              16 : posix_resolver::do_resolve_work(pool_work_item* w) noexcept
     447                 : {
     448              16 :     auto* pw   = static_cast<pool_op*>(w);
     449              16 :     auto* self = pw->resolver_;
     450                 : 
     451              16 :     struct addrinfo hints{};
     452              16 :     hints.ai_family   = AF_UNSPEC;
     453              16 :     hints.ai_socktype = SOCK_STREAM;
     454              16 :     hints.ai_flags    = posix_resolver_detail::flags_to_hints(self->op_.flags);
     455                 : 
     456              16 :     struct addrinfo* ai = nullptr;
     457              48 :     int result          = ::getaddrinfo(
     458              32 :         self->op_.host.empty() ? nullptr : self->op_.host.c_str(),
     459              32 :         self->op_.service.empty() ? nullptr : self->op_.service.c_str(), &hints,
     460                 :         &ai);
     461                 : 
     462              16 :     if (!self->op_.cancelled.load(std::memory_order_acquire))
     463                 :     {
     464              16 :         if (result == 0 && ai)
     465                 :         {
     466              26 :             self->op_.stored_results = posix_resolver_detail::convert_results(
     467              13 :                 ai, self->op_.host, self->op_.service);
     468              13 :             self->op_.gai_error = 0;
     469                 :         }
     470                 :         else
     471                 :         {
     472               3 :             self->op_.gai_error = result;
     473                 :         }
     474                 :     }
     475                 : 
     476              16 :     if (ai)
     477              13 :         ::freeaddrinfo(ai);
     478                 : 
     479                 :     // Move ref to stack before post — post may trigger destroy_impl
     480                 :     // which erases the last shared_ptr, destroying *self (and *pw)
     481              16 :     auto ref = std::move(pw->ref_);
     482              16 :     self->svc_.post(&self->op_);
     483              16 : }
     484                 : 
     485                 : inline void
     486              10 : posix_resolver::do_reverse_resolve_work(pool_work_item* w) noexcept
     487                 : {
     488              10 :     auto* pw   = static_cast<pool_op*>(w);
     489              10 :     auto* self = pw->resolver_;
     490                 : 
     491              10 :     sockaddr_storage ss{};
     492                 :     socklen_t ss_len;
     493                 : 
     494              10 :     if (self->reverse_op_.ep.is_v4())
     495                 :     {
     496               8 :         auto sa = to_sockaddr_in(self->reverse_op_.ep);
     497               8 :         std::memcpy(&ss, &sa, sizeof(sa));
     498               8 :         ss_len = sizeof(sockaddr_in);
     499                 :     }
     500                 :     else
     501                 :     {
     502               2 :         auto sa = to_sockaddr_in6(self->reverse_op_.ep);
     503               2 :         std::memcpy(&ss, &sa, sizeof(sa));
     504               2 :         ss_len = sizeof(sockaddr_in6);
     505                 :     }
     506                 : 
     507                 :     char host[NI_MAXHOST];
     508                 :     char service[NI_MAXSERV];
     509                 : 
     510              10 :     int result = ::getnameinfo(
     511                 :         reinterpret_cast<sockaddr*>(&ss), ss_len, host, sizeof(host), service,
     512                 :         sizeof(service),
     513                 :         posix_resolver_detail::flags_to_ni_flags(self->reverse_op_.flags));
     514                 : 
     515              10 :     if (!self->reverse_op_.cancelled.load(std::memory_order_acquire))
     516                 :     {
     517              10 :         if (result == 0)
     518                 :         {
     519               9 :             self->reverse_op_.stored_host    = host;
     520               9 :             self->reverse_op_.stored_service = service;
     521               9 :             self->reverse_op_.gai_error      = 0;
     522                 :         }
     523                 :         else
     524                 :         {
     525               1 :             self->reverse_op_.gai_error = result;
     526                 :         }
     527                 :     }
     528                 : 
     529                 :     // Move ref to stack before post — post may trigger destroy_impl
     530                 :     // which erases the last shared_ptr, destroying *self (and *pw)
     531              10 :     auto ref = std::move(pw->ref_);
     532              10 :     self->svc_.post(&self->reverse_op_);
     533              10 : }
     534                 : 
     535                 : // posix_resolver_service implementation
     536                 : 
     537                 : inline void
     538             466 : posix_resolver_service::shutdown()
     539                 : {
     540             466 :     std::lock_guard<std::mutex> lock(mutex_);
     541                 : 
     542                 :     // Cancel all resolvers (sets cancelled flag checked by pool threads)
     543             466 :     for (auto* impl = resolver_list_.pop_front(); impl != nullptr;
     544 MIS           0 :          impl       = resolver_list_.pop_front())
     545                 :     {
     546               0 :         impl->cancel();
     547                 :     }
     548                 : 
     549                 :     // Clear the map which releases shared_ptrs.
     550                 :     // The thread pool service shuts down separately via
     551                 :     // execution_context service ordering.
     552 HIT         466 :     resolver_ptrs_.clear();
     553             466 : }
     554                 : 
     555                 : inline io_object::implementation*
     556              29 : posix_resolver_service::construct()
     557                 : {
     558              29 :     auto ptr   = std::make_shared<posix_resolver>(*this);
     559              29 :     auto* impl = ptr.get();
     560                 : 
     561                 :     {
     562              29 :         std::lock_guard<std::mutex> lock(mutex_);
     563              29 :         resolver_list_.push_back(impl);
     564              29 :         resolver_ptrs_[impl] = std::move(ptr);
     565              29 :     }
     566                 : 
     567              29 :     return impl;
     568              29 : }
     569                 : 
     570                 : inline void
     571              29 : posix_resolver_service::destroy_impl(posix_resolver& impl)
     572                 : {
     573              29 :     std::lock_guard<std::mutex> lock(mutex_);
     574              29 :     resolver_list_.remove(&impl);
     575              29 :     resolver_ptrs_.erase(&impl);
     576              29 : }
     577                 : 
     578                 : inline void
     579              26 : posix_resolver_service::post(scheduler_op* op)
     580                 : {
     581              26 :     sched_->post(op);
     582              26 : }
     583                 : 
     584                 : inline void
     585                 : posix_resolver_service::work_started() noexcept
     586                 : {
     587                 :     sched_->work_started();
     588                 : }
     589                 : 
     590                 : inline void
     591              26 : posix_resolver_service::work_finished() noexcept
     592                 : {
     593              26 :     sched_->work_finished();
     594              26 : }
     595                 : 
     596                 : // Free function to get/create the resolver service
     597                 : 
     598                 : inline posix_resolver_service&
     599             466 : get_resolver_service(capy::execution_context& ctx, scheduler& sched)
     600                 : {
     601             466 :     return ctx.make_service<posix_resolver_service>(sched);
     602                 : }
     603                 : 
     604                 : } // namespace boost::corosio::detail
     605                 : 
     606                 : #endif // BOOST_COROSIO_POSIX
     607                 : 
     608                 : #endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_RESOLVER_SERVICE_HPP
        

Generated by: LCOV version 2.3