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

Generated by: LCOV version 2.3